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


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