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

Cache object: e3372f58a519fe61ffc0bbd9bd68ebe7


[ 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.