The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sysv_sem.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: sysv_sem.c,v 1.53 2004/03/23 13:22:04 junyoung Exp $   */
    2 
    3 /*-
    4  * Copyright (c) 1999 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
    9  * NASA Ames Research Center.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *      This product includes software developed by the NetBSD
   22  *      Foundation, Inc. and its contributors.
   23  * 4. Neither the name of The NetBSD Foundation nor the names of its
   24  *    contributors may be used to endorse or promote products derived
   25  *    from this software without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 /*
   41  * Implementation of SVID semaphores
   42  *
   43  * Author: Daniel Boulet
   44  *
   45  * This software is provided ``AS IS'' without any warranties of any kind.
   46  */
   47 
   48 #include <sys/cdefs.h>
   49 __KERNEL_RCSID(0, "$NetBSD: sysv_sem.c,v 1.53 2004/03/23 13:22:04 junyoung Exp $");
   50 
   51 #define SYSVSEM
   52 
   53 #include <sys/param.h>
   54 #include <sys/kernel.h>
   55 #include <sys/sem.h>
   56 #include <sys/sysctl.h>
   57 #include <sys/mount.h>          /* XXX for <sys/syscallargs.h> */
   58 #include <sys/sa.h>
   59 #include <sys/syscallargs.h>
   60 
   61 static int      semtot = 0;
   62 struct  semid_ds *sema;                 /* semaphore id pool */
   63 static struct   __sem *sem;             /* semaphore pool */
   64 static struct   sem_undo *semu_list;    /* list of active undo structures */
   65 static int      *semu;                  /* undo structure pool */
   66 
   67 #ifdef SEM_DEBUG
   68 #define SEM_PRINTF(a) printf a
   69 #else
   70 #define SEM_PRINTF(a)
   71 #endif
   72 
   73 struct sem_undo *semu_alloc(struct proc *);
   74 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
   75 void semundo_clear(int, int);
   76 
   77 /*
   78  * XXXSMP Once we go MP, there needs to be a lock for the semaphore system.
   79  * Until then, we're saved by being a non-preemptive kernel.
   80  */
   81 
   82 void
   83 seminit()
   84 {
   85         int i, sz;
   86         vaddr_t v;
   87 
   88         /* Allocate pageable memory for our structures */
   89         sz = seminfo.semmni * sizeof(struct semid_ds) +
   90             seminfo.semmns * sizeof(struct __sem) +
   91             seminfo.semmnu * seminfo.semusz;
   92         if ((v = uvm_km_zalloc(kernel_map, round_page(sz))) == 0)
   93                 panic("sysv_sem: cannot allocate memory");
   94         sema = (void *)v;
   95         sem = (void *)(sema + seminfo.semmni);
   96         semu = (void *)(sem + seminfo.semmns);
   97 
   98         for (i = 0; i < seminfo.semmni; i++) {
   99                 sema[i]._sem_base = 0;
  100                 sema[i].sem_perm.mode = 0;
  101         }
  102         for (i = 0; i < seminfo.semmnu; i++) {
  103                 struct sem_undo *suptr = SEMU(i);
  104                 suptr->un_proc = NULL;
  105         }
  106         semu_list = NULL;
  107         exithook_establish(semexit, NULL);
  108 }
  109 
  110 /*
  111  * Placebo.
  112  */
  113 
  114 int
  115 sys_semconfig(l, v, retval)
  116         struct lwp *l;
  117         void *v;
  118         register_t *retval;
  119 {
  120 
  121         *retval = 0;
  122         return 0;
  123 }
  124 
  125 /*
  126  * Allocate a new sem_undo structure for a process
  127  * (returns ptr to structure or NULL if no more room)
  128  */
  129 
  130 struct sem_undo *
  131 semu_alloc(p)
  132         struct proc *p;
  133 {
  134         int i;
  135         struct sem_undo *suptr;
  136         struct sem_undo **supptr;
  137         int attempt;
  138 
  139         /*
  140          * Try twice to allocate something.
  141          * (we'll purge any empty structures after the first pass so
  142          * two passes are always enough)
  143          */
  144 
  145         for (attempt = 0; attempt < 2; attempt++) {
  146                 /*
  147                  * Look for a free structure.
  148                  * Fill it in and return it if we find one.
  149                  */
  150 
  151                 for (i = 0; i < seminfo.semmnu; i++) {
  152                         suptr = SEMU(i);
  153                         if (suptr->un_proc == NULL) {
  154                                 suptr->un_next = semu_list;
  155                                 semu_list = suptr;
  156                                 suptr->un_cnt = 0;
  157                                 suptr->un_proc = p;
  158                                 return (suptr);
  159                         }
  160                 }
  161 
  162                 /*
  163                  * We didn't find a free one, if this is the first attempt
  164                  * then try to free some structures.
  165                  */
  166 
  167                 if (attempt == 0) {
  168                         /* All the structures are in use - try to free some */
  169                         int did_something = 0;
  170 
  171                         supptr = &semu_list;
  172                         while ((suptr = *supptr) != NULL) {
  173                                 if (suptr->un_cnt == 0)  {
  174                                         suptr->un_proc = NULL;
  175                                         *supptr = suptr->un_next;
  176                                         did_something = 1;
  177                                 } else
  178                                         supptr = &suptr->un_next;
  179                         }
  180 
  181                         /* If we didn't free anything then just give-up */
  182                         if (!did_something)
  183                                 return (NULL);
  184                 } else {
  185                         /*
  186                          * The second pass failed even though we freed
  187                          * something after the first pass!
  188                          * This is IMPOSSIBLE!
  189                          */
  190                         panic("semu_alloc - second attempt failed");
  191                 }
  192         }
  193         return NULL;
  194 }
  195 
  196 /*
  197  * Adjust a particular entry for a particular proc
  198  */
  199 
  200 int
  201 semundo_adjust(p, supptr, semid, semnum, adjval)
  202         struct proc *p;
  203         struct sem_undo **supptr;
  204         int semid, semnum;
  205         int adjval;
  206 {
  207         struct sem_undo *suptr;
  208         struct undo *sunptr;
  209         int i;
  210 
  211         /*
  212          * Look for and remember the sem_undo if the caller doesn't
  213          * provide it
  214          */
  215 
  216         suptr = *supptr;
  217         if (suptr == NULL) {
  218                 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next)
  219                         if (suptr->un_proc == p)
  220                                 break;
  221 
  222                 if (suptr == NULL) {
  223                         suptr = semu_alloc(p);
  224                         if (suptr == NULL)
  225                                 return (ENOSPC);
  226                 }
  227                 *supptr = suptr;
  228         }
  229 
  230         /*
  231          * Look for the requested entry and adjust it (delete if
  232          * adjval becomes 0).
  233          */
  234         sunptr = &suptr->un_ent[0];
  235         for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
  236                 if (sunptr->un_id != semid || sunptr->un_num != semnum)
  237                         continue;
  238                 sunptr->un_adjval += adjval;
  239                 if (sunptr->un_adjval == 0) {
  240                         suptr->un_cnt--;
  241                         if (i < suptr->un_cnt)
  242                                 suptr->un_ent[i] =
  243                                     suptr->un_ent[suptr->un_cnt];
  244                 }
  245                 return (0);
  246         }
  247 
  248         /* Didn't find the right entry - create it */
  249         if (suptr->un_cnt == SEMUME)
  250                 return (EINVAL);
  251 
  252         sunptr = &suptr->un_ent[suptr->un_cnt];
  253         suptr->un_cnt++;
  254         sunptr->un_adjval = adjval;
  255         sunptr->un_id = semid;
  256         sunptr->un_num = semnum;
  257         return (0);
  258 }
  259 
  260 void
  261 semundo_clear(semid, semnum)
  262         int semid, semnum;
  263 {
  264         struct sem_undo *suptr;
  265         struct undo *sunptr, *sunend;
  266 
  267         for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next)
  268                 for (sunptr = &suptr->un_ent[0],
  269                     sunend = sunptr + suptr->un_cnt; sunptr < sunend;) {
  270                         if (sunptr->un_id == semid) {
  271                                 if (semnum == -1 || sunptr->un_num == semnum) {
  272                                         suptr->un_cnt--;
  273                                         sunend--;
  274                                         if (sunptr != sunend)
  275                                                 *sunptr = *sunend;
  276                                         if (semnum != -1)
  277                                                 break;
  278                                         else
  279                                                 continue;
  280                                 }
  281                         }
  282                         sunptr++;
  283                 }
  284 }
  285 
  286 int
  287 sys_____semctl13(l, v, retval)
  288         struct lwp *l;
  289         void *v;
  290         register_t *retval;
  291 {
  292         struct sys_____semctl13_args /* {
  293                 syscallarg(int) semid;
  294                 syscallarg(int) semnum;
  295                 syscallarg(int) cmd;
  296                 syscallarg(union __semun *) arg;
  297         } */ *uap = v;
  298         struct proc *p = l->l_proc;
  299         struct semid_ds sembuf;
  300         int cmd, error;
  301         void *pass_arg;
  302         union __semun karg;
  303 
  304         cmd = SCARG(uap, cmd);
  305 
  306         switch (cmd) {
  307         case IPC_SET:
  308         case IPC_STAT:
  309                 pass_arg = &sembuf;
  310                 break;
  311 
  312         case GETALL:
  313         case SETVAL:
  314         case SETALL:
  315                 pass_arg = &karg;
  316                 break;
  317         default:
  318                 pass_arg = NULL;
  319                 break;
  320         }
  321 
  322         if (pass_arg) {
  323                 error = copyin(SCARG(uap, arg), &karg, sizeof(karg));
  324                 if (error)
  325                         return error;
  326                 if (cmd == IPC_SET) {
  327                         error = copyin(karg.buf, &sembuf, sizeof(sembuf));
  328                         if (error)
  329                                 return (error);
  330                 }
  331         }
  332 
  333         error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum), cmd,
  334             pass_arg, retval);
  335 
  336         if (error == 0 && cmd == IPC_STAT)
  337                 error = copyout(&sembuf, karg.buf, sizeof(sembuf));
  338 
  339         return (error);
  340 }
  341 
  342 int
  343 semctl1(p, semid, semnum, cmd, v, retval)
  344         struct proc *p;
  345         int semid, semnum, cmd;
  346         void *v;
  347         register_t *retval;
  348 {
  349         struct ucred *cred = p->p_ucred;
  350         union __semun *arg = v;
  351         struct semid_ds *sembuf = v, *semaptr;
  352         int i, error, ix;
  353 
  354         SEM_PRINTF(("call to semctl(%d, %d, %d, %p)\n",
  355             semid, semnum, cmd, v));
  356 
  357         ix = IPCID_TO_IX(semid);
  358         if (ix < 0 || ix >= seminfo.semmni)
  359                 return (EINVAL);
  360 
  361         semaptr = &sema[ix];
  362         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
  363             semaptr->sem_perm._seq != IPCID_TO_SEQ(semid))
  364                 return (EINVAL);
  365 
  366         switch (cmd) {
  367         case IPC_RMID:
  368                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
  369                         return (error);
  370                 semaptr->sem_perm.cuid = cred->cr_uid;
  371                 semaptr->sem_perm.uid = cred->cr_uid;
  372                 semtot -= semaptr->sem_nsems;
  373                 for (i = semaptr->_sem_base - sem; i < semtot; i++)
  374                         sem[i] = sem[i + semaptr->sem_nsems];
  375                 for (i = 0; i < seminfo.semmni; i++) {
  376                         if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
  377                             sema[i]._sem_base > semaptr->_sem_base)
  378                                 sema[i]._sem_base -= semaptr->sem_nsems;
  379                 }
  380                 semaptr->sem_perm.mode = 0;
  381                 semundo_clear(ix, -1);
  382                 wakeup(semaptr);
  383                 break;
  384 
  385         case IPC_SET:
  386                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
  387                         return (error);
  388                 semaptr->sem_perm.uid = sembuf->sem_perm.uid;
  389                 semaptr->sem_perm.gid = sembuf->sem_perm.gid;
  390                 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
  391                     (sembuf->sem_perm.mode & 0777);
  392                 semaptr->sem_ctime = time.tv_sec;
  393                 break;
  394 
  395         case IPC_STAT:
  396                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  397                         return (error);
  398                 memcpy(sembuf, semaptr, sizeof(struct semid_ds));
  399                 break;
  400 
  401         case GETNCNT:
  402                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  403                         return (error);
  404                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  405                         return (EINVAL);
  406                 *retval = semaptr->_sem_base[semnum].semncnt;
  407                 break;
  408 
  409         case GETPID:
  410                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  411                         return (error);
  412                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  413                         return (EINVAL);
  414                 *retval = semaptr->_sem_base[semnum].sempid;
  415                 break;
  416 
  417         case GETVAL:
  418                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  419                         return (error);
  420                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  421                         return (EINVAL);
  422                 *retval = semaptr->_sem_base[semnum].semval;
  423                 break;
  424 
  425         case GETALL:
  426                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  427                         return (error);
  428                 for (i = 0; i < semaptr->sem_nsems; i++) {
  429                         error = copyout(&semaptr->_sem_base[i].semval,
  430                             &arg->array[i], sizeof(arg->array[i]));
  431                         if (error != 0)
  432                                 break;
  433                 }
  434                 break;
  435 
  436         case GETZCNT:
  437                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  438                         return (error);
  439                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  440                         return (EINVAL);
  441                 *retval = semaptr->_sem_base[semnum].semzcnt;
  442                 break;
  443 
  444         case SETVAL:
  445                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  446                         return (error);
  447                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  448                         return (EINVAL);
  449                 semaptr->_sem_base[semnum].semval = arg->val;
  450                 semundo_clear(ix, semnum);
  451                 wakeup(semaptr);
  452                 break;
  453 
  454         case SETALL:
  455                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  456                         return (error);
  457                 for (i = 0; i < semaptr->sem_nsems; i++) {
  458                         error = copyin(&arg->array[i],
  459                             &semaptr->_sem_base[i].semval,
  460                             sizeof(arg->array[i]));
  461                         if (error != 0)
  462                                 break;
  463                 }
  464                 semundo_clear(ix, -1);
  465                 wakeup(semaptr);
  466                 break;
  467 
  468         default:
  469                 return (EINVAL);
  470         }
  471 
  472         return (error);
  473 }
  474 
  475 int
  476 sys_semget(l, v, retval)
  477         struct lwp *l;
  478         void *v;
  479         register_t *retval;
  480 {
  481         struct sys_semget_args /* {
  482                 syscallarg(key_t) key;
  483                 syscallarg(int) nsems;
  484                 syscallarg(int) semflg;
  485         } */ *uap = v;
  486         int semid, eval;
  487         int key = SCARG(uap, key);
  488         int nsems = SCARG(uap, nsems);
  489         int semflg = SCARG(uap, semflg);
  490         struct ucred *cred = l->l_proc->p_ucred;
  491 
  492         SEM_PRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
  493 
  494         if (key != IPC_PRIVATE) {
  495                 for (semid = 0; semid < seminfo.semmni; semid++) {
  496                         if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
  497                             sema[semid].sem_perm._key == key)
  498                                 break;
  499                 }
  500                 if (semid < seminfo.semmni) {
  501                         SEM_PRINTF(("found public key\n"));
  502                         if ((eval = ipcperm(cred, &sema[semid].sem_perm,
  503                             semflg & 0700)))
  504                                 return (eval);
  505                         if (nsems > 0 && sema[semid].sem_nsems < nsems) {
  506                                 SEM_PRINTF(("too small\n"));
  507                                 return (EINVAL);
  508                         }
  509                         if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
  510                                 SEM_PRINTF(("not exclusive\n"));
  511                                 return (EEXIST);
  512                         }
  513                         goto found;
  514                 }
  515         }
  516 
  517         SEM_PRINTF(("need to allocate the semid_ds\n"));
  518         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
  519                 if (nsems <= 0 || nsems > seminfo.semmsl) {
  520                         SEM_PRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
  521                             seminfo.semmsl));
  522                         return (EINVAL);
  523                 }
  524                 if (nsems > seminfo.semmns - semtot) {
  525                         SEM_PRINTF(("not enough semaphores left "
  526                             "(need %d, got %d)\n",
  527                             nsems, seminfo.semmns - semtot));
  528                         return (ENOSPC);
  529                 }
  530                 for (semid = 0; semid < seminfo.semmni; semid++) {
  531                         if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
  532                                 break;
  533                 }
  534                 if (semid == seminfo.semmni) {
  535                         SEM_PRINTF(("no more semid_ds's available\n"));
  536                         return (ENOSPC);
  537                 }
  538                 SEM_PRINTF(("semid %d is available\n", semid));
  539                 sema[semid].sem_perm._key = key;
  540                 sema[semid].sem_perm.cuid = cred->cr_uid;
  541                 sema[semid].sem_perm.uid = cred->cr_uid;
  542                 sema[semid].sem_perm.cgid = cred->cr_gid;
  543                 sema[semid].sem_perm.gid = cred->cr_gid;
  544                 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
  545                 sema[semid].sem_perm._seq =
  546                     (sema[semid].sem_perm._seq + 1) & 0x7fff;
  547                 sema[semid].sem_nsems = nsems;
  548                 sema[semid].sem_otime = 0;
  549                 sema[semid].sem_ctime = time.tv_sec;
  550                 sema[semid]._sem_base = &sem[semtot];
  551                 semtot += nsems;
  552                 memset(sema[semid]._sem_base, 0,
  553                     sizeof(sema[semid]._sem_base[0]) * nsems);
  554                 SEM_PRINTF(("sembase = %p, next = %p\n", sema[semid]._sem_base,
  555                     &sem[semtot]));
  556         } else {
  557                 SEM_PRINTF(("didn't find it and wasn't asked to create it\n"));
  558                 return (ENOENT);
  559         }
  560 
  561 found:
  562         *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
  563         return (0);
  564 }
  565 
  566 int
  567 sys_semop(l, v, retval)
  568         struct lwp *l;
  569         void *v;
  570         register_t *retval;
  571 {
  572         struct sys_semop_args /* {
  573                 syscallarg(int) semid;
  574                 syscallarg(struct sembuf *) sops;
  575                 syscallarg(size_t) nsops;
  576         } */ *uap = v;
  577         struct proc *p = l->l_proc;
  578         int semid = SCARG(uap, semid), seq;
  579         size_t nsops = SCARG(uap, nsops);
  580         struct sembuf sops[MAX_SOPS];
  581         struct semid_ds *semaptr;
  582         struct sembuf *sopptr = NULL;
  583         struct __sem *semptr = NULL;
  584         struct sem_undo *suptr = NULL;
  585         struct ucred *cred = p->p_ucred;
  586         int i, j, eval;
  587         int do_wakeup, do_undos;
  588 
  589         SEM_PRINTF(("call to semop(%d, %p, %lld)\n", semid, sops,
  590             (long long)nsops));
  591 
  592         semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
  593         if (semid < 0 || semid >= seminfo.semmni)
  594                 return (EINVAL);
  595 
  596         semaptr = &sema[semid];
  597         seq = IPCID_TO_SEQ(SCARG(uap, semid));
  598         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
  599             semaptr->sem_perm._seq != seq)
  600                 return (EINVAL);
  601 
  602         if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
  603                 SEM_PRINTF(("eval = %d from ipaccess\n", eval));
  604                 return (eval);
  605         }
  606 
  607         if (nsops > MAX_SOPS) {
  608                 SEM_PRINTF(("too many sops (max=%d, nsops=%lld)\n", MAX_SOPS,
  609                     (long long)nsops));
  610                 return (E2BIG);
  611         }
  612 
  613         if ((eval = copyin(SCARG(uap, sops),
  614             sops, nsops * sizeof(sops[0]))) != 0) {
  615                 SEM_PRINTF(("eval = %d from copyin(%p, %p, %lld)\n", eval,
  616                     SCARG(uap, sops), &sops,
  617                     (long long)(nsops * sizeof(sops[0]))));
  618                 return (eval);
  619         }
  620 
  621         for (i = 0; i < nsops; i++)
  622                 if (sops[i].sem_num >= semaptr->sem_nsems)
  623                         return (EFBIG);
  624 
  625         /*
  626          * Loop trying to satisfy the vector of requests.
  627          * If we reach a point where we must wait, any requests already
  628          * performed are rolled back and we go to sleep until some other
  629          * process wakes us up.  At this point, we start all over again.
  630          *
  631          * This ensures that from the perspective of other tasks, a set
  632          * of requests is atomic (never partially satisfied).
  633          */
  634         do_undos = 0;
  635 
  636         for (;;) {
  637                 do_wakeup = 0;
  638 
  639                 for (i = 0; i < nsops; i++) {
  640                         sopptr = &sops[i];
  641                         semptr = &semaptr->_sem_base[sopptr->sem_num];
  642 
  643                         SEM_PRINTF(("semop:  semaptr=%p, sem_base=%p, "
  644                             "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n",
  645                             semaptr, semaptr->_sem_base, semptr,
  646                             sopptr->sem_num, semptr->semval, sopptr->sem_op,
  647                             (sopptr->sem_flg & IPC_NOWAIT) ?
  648                             "nowait" : "wait"));
  649 
  650                         if (sopptr->sem_op < 0) {
  651                                 if ((int)(semptr->semval +
  652                                     sopptr->sem_op) < 0) {
  653                                         SEM_PRINTF(("semop:  "
  654                                             "can't do it now\n"));
  655                                         break;
  656                                 } else {
  657                                         semptr->semval += sopptr->sem_op;
  658                                         if (semptr->semval == 0 &&
  659                                             semptr->semzcnt > 0)
  660                                                 do_wakeup = 1;
  661                                 }
  662                                 if (sopptr->sem_flg & SEM_UNDO)
  663                                         do_undos = 1;
  664                         } else if (sopptr->sem_op == 0) {
  665                                 if (semptr->semval > 0) {
  666                                         SEM_PRINTF(("semop:  not zero now\n"));
  667                                         break;
  668                                 }
  669                         } else {
  670                                 if (semptr->semncnt > 0)
  671                                         do_wakeup = 1;
  672                                 semptr->semval += sopptr->sem_op;
  673                                 if (sopptr->sem_flg & SEM_UNDO)
  674                                         do_undos = 1;
  675                         }
  676                 }
  677 
  678                 /*
  679                  * Did we get through the entire vector?
  680                  */
  681                 if (i >= nsops)
  682                         goto done;
  683 
  684                 /*
  685                  * No ... rollback anything that we've already done
  686                  */
  687                 SEM_PRINTF(("semop:  rollback 0 through %d\n", i - 1));
  688                 while (i-- > 0)
  689                         semaptr->_sem_base[sops[i].sem_num].semval -=
  690                             sops[i].sem_op;
  691 
  692                 /*
  693                  * If the request that we couldn't satisfy has the
  694                  * NOWAIT flag set then return with EAGAIN.
  695                  */
  696                 if (sopptr->sem_flg & IPC_NOWAIT)
  697                         return (EAGAIN);
  698 
  699                 if (sopptr->sem_op == 0)
  700                         semptr->semzcnt++;
  701                 else
  702                         semptr->semncnt++;
  703 
  704                 SEM_PRINTF(("semop:  good night!\n"));
  705                 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
  706                     "semwait", 0);
  707                 SEM_PRINTF(("semop:  good morning (eval=%d)!\n", eval));
  708 
  709                 /*
  710                  * Make sure that the semaphore still exists
  711                  */
  712                 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
  713                     semaptr->sem_perm._seq != seq) {
  714                         /* The man page says to return EIDRM. */
  715                         /* Unfortunately, BSD doesn't define that code! */
  716 #ifdef EIDRM
  717                         return (EIDRM);
  718 #else
  719                         return (EINVAL);
  720 #endif
  721                 }
  722 
  723                 /*
  724                  * The semaphore is still alive.  Readjust the count of
  725                  * waiting processes.
  726                  */
  727                 semptr = &semaptr->_sem_base[sopptr->sem_num];
  728                 if (sopptr->sem_op == 0)
  729                         semptr->semzcnt--;
  730                 else
  731                         semptr->semncnt--;
  732                 /*
  733                  * Is it really morning, or was our sleep interrupted?
  734                  * (Delayed check of tsleep() return code because we
  735                  * need to decrement sem[nz]cnt either way.)
  736                  */
  737                 if (eval != 0)
  738                         return (EINTR);
  739                 SEM_PRINTF(("semop:  good morning!\n"));
  740         }
  741 
  742 done:
  743         /*
  744          * Process any SEM_UNDO requests.
  745          */
  746         if (do_undos) {
  747                 for (i = 0; i < nsops; i++) {
  748                         /*
  749                          * We only need to deal with SEM_UNDO's for non-zero
  750                          * op's.
  751                          */
  752                         int adjval;
  753 
  754                         if ((sops[i].sem_flg & SEM_UNDO) == 0)
  755                                 continue;
  756                         adjval = sops[i].sem_op;
  757                         if (adjval == 0)
  758                                 continue;
  759                         eval = semundo_adjust(p, &suptr, semid,
  760                             sops[i].sem_num, -adjval);
  761                         if (eval == 0)
  762                                 continue;
  763 
  764                         /*
  765                          * Oh-Oh!  We ran out of either sem_undo's or undo's.
  766                          * Rollback the adjustments to this point and then
  767                          * rollback the semaphore ups and down so we can return
  768                          * with an error with all structures restored.  We
  769                          * rollback the undo's in the exact reverse order that
  770                          * we applied them.  This guarantees that we won't run
  771                          * out of space as we roll things back out.
  772                          */
  773                         while (i-- > 0) {
  774                                 if ((sops[i].sem_flg & SEM_UNDO) == 0)
  775                                         continue;
  776                                 adjval = sops[i].sem_op;
  777                                 if (adjval == 0)
  778                                         continue;
  779                                 if (semundo_adjust(p, &suptr, semid,
  780                                     sops[i].sem_num, adjval) != 0)
  781                                         panic("semop - can't undo undos");
  782                         }
  783 
  784                         for (j = 0; j < nsops; j++)
  785                                 semaptr->_sem_base[sops[j].sem_num].semval -=
  786                                     sops[j].sem_op;
  787 
  788                         SEM_PRINTF(("eval = %d from semundo_adjust\n", eval));
  789                         return (eval);
  790                 } /* loop through the sops */
  791         } /* if (do_undos) */
  792 
  793         /* We're definitely done - set the sempid's */
  794         for (i = 0; i < nsops; i++) {
  795                 sopptr = &sops[i];
  796                 semptr = &semaptr->_sem_base[sopptr->sem_num];
  797                 semptr->sempid = p->p_pid;
  798         }
  799 
  800         /* Do a wakeup if any semaphore was up'd. */
  801         if (do_wakeup) {
  802                 SEM_PRINTF(("semop:  doing wakeup\n"));
  803 #ifdef SEM_WAKEUP
  804                 sem_wakeup((caddr_t)semaptr);
  805 #else
  806                 wakeup((caddr_t)semaptr);
  807 #endif
  808                 SEM_PRINTF(("semop:  back from wakeup\n"));
  809         }
  810         SEM_PRINTF(("semop:  done\n"));
  811         *retval = 0;
  812         return (0);
  813 }
  814 
  815 /*
  816  * Go through the undo structures for this process and apply the
  817  * adjustments to semaphores.
  818  */
  819 /*ARGSUSED*/
  820 void
  821 semexit(p, v)
  822         struct proc *p;
  823         void *v;
  824 {
  825         struct sem_undo *suptr;
  826         struct sem_undo **supptr;
  827 
  828         /*
  829          * Go through the chain of undo vectors looking for one
  830          * associated with this process.
  831          */
  832 
  833         for (supptr = &semu_list; (suptr = *supptr) != NULL;
  834             supptr = &suptr->un_next) {
  835                 if (suptr->un_proc == p)
  836                         break;
  837         }
  838 
  839         /*
  840          * If there is no undo vector, skip to the end.
  841          */
  842 
  843         if (suptr == NULL)
  844                 return;
  845 
  846         /*
  847          * We now have an undo vector for this process.
  848          */
  849 
  850         SEM_PRINTF(("proc @%p has undo structure with %d entries\n", p,
  851             suptr->un_cnt));
  852 
  853         /*
  854          * If there are any active undo elements then process them.
  855          */
  856         if (suptr->un_cnt > 0) {
  857                 int ix;
  858 
  859                 for (ix = 0; ix < suptr->un_cnt; ix++) {
  860                         int semid = suptr->un_ent[ix].un_id;
  861                         int semnum = suptr->un_ent[ix].un_num;
  862                         int adjval = suptr->un_ent[ix].un_adjval;
  863                         struct semid_ds *semaptr;
  864 
  865                         semaptr = &sema[semid];
  866                         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
  867                                 panic("semexit - semid not allocated");
  868                         if (semnum >= semaptr->sem_nsems)
  869                                 panic("semexit - semnum out of range");
  870 
  871                         SEM_PRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; "
  872                             "sem=%d\n",
  873                             suptr->un_proc, suptr->un_ent[ix].un_id,
  874                             suptr->un_ent[ix].un_num,
  875                             suptr->un_ent[ix].un_adjval,
  876                             semaptr->_sem_base[semnum].semval));
  877 
  878                         if (adjval < 0 &&
  879                             semaptr->_sem_base[semnum].semval < -adjval)
  880                                 semaptr->_sem_base[semnum].semval = 0;
  881                         else
  882                                 semaptr->_sem_base[semnum].semval += adjval;
  883 
  884 #ifdef SEM_WAKEUP
  885                         sem_wakeup((caddr_t)semaptr);
  886 #else
  887                         wakeup((caddr_t)semaptr);
  888 #endif
  889                         SEM_PRINTF(("semexit:  back from wakeup\n"));
  890                 }
  891         }
  892 
  893         /*
  894          * Deallocate the undo vector.
  895          */
  896         SEM_PRINTF(("removing vector\n"));
  897         suptr->un_proc = NULL;
  898         *supptr = suptr->un_next;
  899 }

Cache object: 0198a510b2cbde7390d8ef27bcb6acfc


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.