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 /*      $OpenBSD: sysv_sem.c,v 1.63 2022/09/28 13:21:13 mbuhl Exp $     */
    2 /*      $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $   */
    3 
    4 /*
    5  * Copyright (c) 2002,2003 Todd C. Miller <millert@openbsd.org>
    6  *
    7  * Permission to use, copy, modify, and distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  *
   19  * Sponsored in part by the Defense Advanced Research Projects
   20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
   21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
   22  */
   23 /*
   24  * Implementation of SVID semaphores
   25  *
   26  * Author:  Daniel Boulet
   27  *
   28  * This software is provided ``AS IS'' without any warranties of any kind.
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/proc.h>
   34 #include <sys/sem.h>
   35 #include <sys/sysctl.h>
   36 #include <sys/malloc.h>
   37 #include <sys/pool.h>
   38 
   39 #include <sys/mount.h>
   40 #include <sys/syscallargs.h>
   41 
   42 #ifdef SEM_DEBUG
   43 #define DPRINTF(x)      printf x
   44 #else
   45 #define DPRINTF(x)
   46 #endif
   47 
   48 int     semtot = 0;
   49 int     semutot = 0;
   50 struct  semid_ds **sema;        /* semaphore id list */
   51 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
   52 struct  pool sema_pool;         /* pool for struct semid_ds */
   53 struct  pool semu_pool;         /* pool for struct sem_undo (SEMUSZ) */
   54 unsigned short *semseqs;        /* array of sem sequence numbers */
   55 
   56 struct sem_undo *semu_alloc(struct process *);
   57 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
   58 void semundo_clear(int, int);
   59 
   60 void
   61 seminit(void)
   62 {
   63 
   64         pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
   65             "semapl", NULL);
   66         pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
   67         sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
   68             M_SEM, M_WAITOK|M_ZERO);
   69         semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
   70             M_SEM, M_WAITOK|M_ZERO);
   71         SLIST_INIT(&semu_list);
   72 }
   73 
   74 /*
   75  * Allocate a new sem_undo structure for a process
   76  * (returns ptr to structure or NULL if no more room)
   77  */
   78 struct sem_undo *
   79 semu_alloc(struct process *pr)
   80 {
   81         struct sem_undo *suptr, *sutmp;
   82 
   83         if (semutot == seminfo.semmnu)
   84                 return (NULL);          /* no space */
   85 
   86         /*
   87          * Allocate a semu w/o waiting if possible.
   88          * If we do have to wait, we must check to verify that a semu
   89          * with un_proc == pr has not been allocated in the meantime.
   90          */
   91         semutot++;
   92         if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) {
   93                 sutmp = pool_get(&semu_pool, PR_WAITOK);
   94                 SLIST_FOREACH(suptr, &semu_list, un_next) {
   95                         if (suptr->un_proc == pr) {
   96                                 pool_put(&semu_pool, sutmp);
   97                                 semutot--;
   98                                 return (suptr);
   99                         }
  100                 }
  101                 suptr = sutmp;
  102         }
  103         suptr->un_cnt = 0;
  104         suptr->un_proc = pr;
  105         SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
  106         return (suptr);
  107 }
  108 
  109 /*
  110  * Adjust a particular entry for a particular proc
  111  */
  112 int
  113 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
  114         int adjval)
  115 {
  116         struct process *pr = p->p_p;
  117         struct sem_undo *suptr;
  118         struct undo *sunptr;
  119         int i;
  120 
  121         /*
  122          * Look for and remember the sem_undo if the caller doesn't provide it.
  123          */
  124         suptr = *supptr;
  125         if (suptr == NULL) {
  126                 SLIST_FOREACH(suptr, &semu_list, un_next) {
  127                         if (suptr->un_proc == pr) {
  128                                 *supptr = suptr;
  129                                 break;
  130                         }
  131                 }
  132                 if (suptr == NULL) {
  133                         if (adjval == 0)
  134                                 return (0);
  135                         suptr = semu_alloc(p->p_p);
  136                         if (suptr == NULL)
  137                                 return (ENOSPC);
  138                         *supptr = suptr;
  139                 }
  140         }
  141 
  142         /*
  143          * Look for the requested entry and adjust it
  144          * (delete if adjval becomes 0).
  145          */
  146         sunptr = &suptr->un_ent[0];
  147         for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
  148                 if (sunptr->un_id != semid || sunptr->un_num != semnum)
  149                         continue;
  150                 if (adjval == 0)
  151                         sunptr->un_adjval = 0;
  152                 else
  153                         sunptr->un_adjval += adjval;
  154                 if (sunptr->un_adjval != 0)
  155                         return (0);
  156 
  157                 if (--suptr->un_cnt == 0) {
  158                         *supptr = NULL;
  159                         SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
  160                         pool_put(&semu_pool, suptr);
  161                         semutot--;
  162                 } else if (i < suptr->un_cnt)
  163                         suptr->un_ent[i] =
  164                             suptr->un_ent[suptr->un_cnt];
  165                 return (0);
  166         }
  167 
  168         /* Didn't find the right entry - create it */
  169         if (adjval == 0)
  170                 return (0);
  171         if (suptr->un_cnt == SEMUME)
  172                 return (EINVAL);
  173 
  174         sunptr = &suptr->un_ent[suptr->un_cnt];
  175         suptr->un_cnt++;
  176         sunptr->un_adjval = adjval;
  177         sunptr->un_id = semid;
  178         sunptr->un_num = semnum;
  179         return (0);
  180 }
  181 
  182 void
  183 semundo_clear(int semid, int semnum)
  184 {
  185         struct sem_undo *suptr = SLIST_FIRST(&semu_list);
  186         struct sem_undo *suprev = NULL;
  187         struct undo *sunptr;
  188         int i;
  189 
  190         while (suptr != NULL) {
  191                 sunptr = &suptr->un_ent[0];
  192                 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
  193                         if (sunptr->un_id == semid) {
  194                                 if (semnum == -1 || sunptr->un_num == semnum) {
  195                                         suptr->un_cnt--;
  196                                         if (i < suptr->un_cnt) {
  197                                                 suptr->un_ent[i] =
  198                                                   suptr->un_ent[suptr->un_cnt];
  199                                                 i--, sunptr--;
  200                                         }
  201                                 }
  202                                 if (semnum != -1)
  203                                         break;
  204                         }
  205                 }
  206                 if (suptr->un_cnt == 0) {
  207                         struct sem_undo *sutmp = suptr;
  208 
  209                         if (suptr == SLIST_FIRST(&semu_list))
  210                                 SLIST_REMOVE_HEAD(&semu_list, un_next);
  211                         else
  212                                 SLIST_REMOVE_AFTER(suprev, un_next);
  213                         suptr = SLIST_NEXT(suptr, un_next);
  214                         pool_put(&semu_pool, sutmp);
  215                         semutot--;
  216                 } else {
  217                         suprev = suptr;
  218                         suptr = SLIST_NEXT(suptr, un_next);
  219                 }
  220         }
  221 }
  222 
  223 int
  224 sys___semctl(struct proc *p, void *v, register_t *retval)
  225 {
  226         struct sys___semctl_args /* {
  227                 syscallarg(int) semid;
  228                 syscallarg(int) semnum;
  229                 syscallarg(int) cmd;
  230                 syscallarg(union semun *) arg;
  231         } */ *uap = v;
  232         struct ucred *cred = p->p_ucred;
  233         int semid = SCARG(uap, semid);
  234         int semnum = SCARG(uap, semnum);
  235         int cmd = SCARG(uap, cmd);
  236         union semun arg, *uarg = SCARG(uap, arg);
  237         struct semid_ds sbuf;
  238         struct semid_ds *semaptr;
  239         unsigned short *semval = NULL, nsems;
  240         int i, ix, error;
  241 
  242         switch (cmd) {
  243         case IPC_SET:
  244         case IPC_STAT:
  245         case GETALL:
  246         case SETVAL:
  247         case SETALL:
  248                 if ((error = copyin(uarg, &arg, sizeof(union semun))))
  249                         return (error);
  250         }
  251         if (cmd == IPC_SET)
  252                 if ((error = copyin(arg.buf, &sbuf, sizeof(sbuf))))
  253                         return (error);
  254 
  255         DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, uarg));
  256 
  257         ix = IPCID_TO_IX(semid);
  258         if (ix < 0 || ix >= seminfo.semmni)
  259                 return (EINVAL);
  260 
  261 again:
  262         if ((semaptr = sema[ix]) == NULL ||
  263             semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
  264                 return (EINVAL);
  265 
  266         switch (cmd) {
  267         case IPC_RMID:
  268                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
  269                         return (error);
  270                 semaptr->sem_perm.cuid = cred->cr_uid;
  271                 semaptr->sem_perm.uid = cred->cr_uid;
  272                 semtot -= semaptr->sem_nsems;
  273                 free(semaptr->sem_base, M_SEM,
  274                     semaptr->sem_nsems * sizeof(struct sem));
  275                 pool_put(&sema_pool, semaptr);
  276                 sema[ix] = NULL;
  277                 semundo_clear(ix, -1);
  278                 wakeup(&sema[ix]);
  279                 break;
  280 
  281         case IPC_SET:
  282                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
  283                         return (error);
  284                 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
  285                 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
  286                 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
  287                     (sbuf.sem_perm.mode & 0777);
  288                 semaptr->sem_ctime = gettime();
  289                 break;
  290 
  291         case IPC_STAT:
  292                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  293                         return (error);
  294                 memcpy(&sbuf, semaptr, sizeof sbuf);
  295                 sbuf.sem_base = NULL;
  296                 error = copyout(&sbuf, arg.buf, sizeof(struct semid_ds));
  297                 break;
  298 
  299         case GETNCNT:
  300                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  301                         return (error);
  302                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  303                         return (EINVAL);
  304                 *retval = semaptr->sem_base[semnum].semncnt;
  305                 break;
  306 
  307         case GETPID:
  308                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  309                         return (error);
  310                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  311                         return (EINVAL);
  312                 *retval = semaptr->sem_base[semnum].sempid;
  313                 break;
  314 
  315         case GETVAL:
  316                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  317                         return (error);
  318                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  319                         return (EINVAL);
  320                 *retval = semaptr->sem_base[semnum].semval;
  321                 break;
  322 
  323         case GETALL:
  324                 nsems = semaptr->sem_nsems;
  325                 semval = mallocarray(nsems, sizeof(arg.array[0]),
  326                     M_TEMP, M_WAITOK);
  327                 if (semaptr != sema[ix] || 
  328                     semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
  329                     semaptr->sem_nsems != nsems) {
  330                         free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
  331                         goto again;
  332                 }
  333                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  334                         goto error;
  335                 for (i = 0; i < nsems; i++)
  336                         semval[i] = semaptr->sem_base[i].semval;
  337                 for (i = 0; i < nsems; i++) {
  338                         error = copyout(&semval[i], &arg.array[i],
  339                             sizeof(arg.array[0]));
  340                         if (error != 0)
  341                                 break;
  342                 }
  343                 break;
  344 
  345         case GETZCNT:
  346                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  347                         return (error);
  348                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  349                         return (EINVAL);
  350                 *retval = semaptr->sem_base[semnum].semzcnt;
  351                 break;
  352 
  353         case SETVAL:
  354                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  355                         return (error);
  356                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  357                         return (EINVAL);
  358                 if (arg.val > seminfo.semvmx)
  359                         return (ERANGE);
  360                 semaptr->sem_base[semnum].semval = arg.val;
  361                 semundo_clear(ix, semnum);
  362                 wakeup(&sema[ix]);
  363                 break;
  364 
  365         case SETALL:
  366                 nsems = semaptr->sem_nsems;
  367                 semval = mallocarray(nsems, sizeof(arg.array[0]),
  368                     M_TEMP, M_WAITOK);
  369                 for (i = 0; i < nsems; i++) {
  370                         error = copyin(&arg.array[i], &semval[i],
  371                             sizeof(arg.array[0]));
  372                         if (error != 0)
  373                                 goto error;
  374                         if (semval[i] > seminfo.semvmx) {
  375                                 error = ERANGE;
  376                                 goto error;
  377                         }
  378                 }
  379                 if (semaptr != sema[ix] ||
  380                     semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
  381                     semaptr->sem_nsems != nsems) {
  382                         free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
  383                         goto again;
  384                 }
  385                 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  386                         goto error;
  387                 for (i = 0; i < nsems; i++)
  388                         semaptr->sem_base[i].semval = semval[i];
  389                 semundo_clear(ix, -1);
  390                 wakeup(&sema[ix]);
  391                 break;
  392 
  393         default:
  394                 return (EINVAL);
  395         }
  396 
  397 error:
  398         free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
  399 
  400         return (error);
  401 }
  402 
  403 int
  404 sys_semget(struct proc *p, void *v, register_t *retval)
  405 {
  406         struct sys_semget_args /* {
  407                 syscallarg(key_t) key;
  408                 syscallarg(int) nsems;
  409                 syscallarg(int) semflg;
  410         } */ *uap = v;
  411         int semid, error;
  412         int key = SCARG(uap, key);
  413         int nsems = SCARG(uap, nsems);
  414         int semflg = SCARG(uap, semflg);
  415         struct semid_ds *semaptr, *semaptr_new = NULL;
  416         struct ucred *cred = p->p_ucred;
  417 
  418         DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
  419 
  420         /*
  421          * Preallocate space for the new semaphore.  If we are going
  422          * to sleep, we want to sleep now to eliminate any race
  423          * condition in allocating a semaphore with a specific key.
  424          */
  425         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
  426                 if (nsems <= 0 || nsems > seminfo.semmsl) {
  427                         DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
  428                             seminfo.semmsl));
  429                         return (EINVAL);
  430                 }
  431                 if (nsems > seminfo.semmns - semtot) {
  432                         DPRINTF(("not enough semaphores left (need %d, got %d)\n",
  433                             nsems, seminfo.semmns - semtot));
  434                         return (ENOSPC);
  435                 }
  436                 semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO);
  437                 semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
  438                     M_SEM, M_WAITOK|M_ZERO);
  439                 if (nsems > seminfo.semmns - semtot) {
  440                         error = ENOSPC;
  441                         goto error;
  442                 }
  443         }
  444 
  445         if (key != IPC_PRIVATE) {
  446                 for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
  447                         if ((semaptr = sema[semid]) != NULL &&
  448                             semaptr->sem_perm.key == key) {
  449                                 DPRINTF(("found public key\n"));
  450                                 if ((error = ipcperm(cred, &semaptr->sem_perm,
  451                                     semflg & 0700)))
  452                                         goto error;
  453                                 if (nsems > 0 && semaptr->sem_nsems < nsems) {
  454                                         DPRINTF(("too small\n"));
  455                                         error = EINVAL;
  456                                         goto error;
  457                                 }
  458                                 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
  459                                         DPRINTF(("not exclusive\n"));
  460                                         error = EEXIST;
  461                                         goto error;
  462                                 }
  463                                 if (semaptr_new != NULL) {
  464                                         free(semaptr_new->sem_base, M_SEM,
  465                                             nsems * sizeof(struct sem));
  466                                         pool_put(&sema_pool, semaptr_new);
  467                                 }
  468                                 goto found;
  469                         }
  470                 }
  471         }
  472 
  473         DPRINTF(("need to allocate the semid_ds\n"));
  474         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
  475                 for (semid = 0; semid < seminfo.semmni; semid++) {
  476                         if ((semaptr = sema[semid]) == NULL)
  477                                 break;
  478                 }
  479                 if (semid == seminfo.semmni) {
  480                         DPRINTF(("no more semid_ds's available\n"));
  481                         error = ENOSPC;
  482                         goto error;
  483                 }
  484                 DPRINTF(("semid %d is available\n", semid));
  485                 semaptr_new->sem_perm.key = key;
  486                 semaptr_new->sem_perm.cuid = cred->cr_uid;
  487                 semaptr_new->sem_perm.uid = cred->cr_uid;
  488                 semaptr_new->sem_perm.cgid = cred->cr_gid;
  489                 semaptr_new->sem_perm.gid = cred->cr_gid;
  490                 semaptr_new->sem_perm.mode = (semflg & 0777);
  491                 semaptr_new->sem_perm.seq = semseqs[semid] =
  492                     (semseqs[semid] + 1) & 0x7fff;
  493                 semaptr_new->sem_nsems = nsems;
  494                 semaptr_new->sem_otime = 0;
  495                 semaptr_new->sem_ctime = gettime();
  496                 sema[semid] = semaptr_new;
  497                 semtot += nsems;
  498         } else {
  499                 DPRINTF(("didn't find it and wasn't asked to create it\n"));
  500                 return (ENOENT);
  501         }
  502 
  503 found:
  504         *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
  505         return (0);
  506 error:
  507         if (semaptr_new != NULL) {
  508                 free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem));
  509                 pool_put(&sema_pool, semaptr_new);
  510         }
  511         return (error);
  512 }
  513 
  514 int
  515 sys_semop(struct proc *p, void *v, register_t *retval)
  516 {
  517         struct sys_semop_args /* {
  518                 syscallarg(int) semid;
  519                 syscallarg(struct sembuf *) sops;
  520                 syscallarg(size_t) nsops;
  521         } */ *uap = v;
  522 #define NSOPS   8
  523         struct sembuf sopbuf[NSOPS];
  524         int semid = SCARG(uap, semid);
  525         size_t nsops = SCARG(uap, nsops);
  526         struct sembuf *sops;
  527         struct semid_ds *semaptr;
  528         struct sembuf *sopptr = NULL;
  529         struct sem *semptr = NULL;
  530         struct sem_undo *suptr = NULL;
  531         struct ucred *cred = p->p_ucred;
  532         size_t i, j;
  533         int do_wakeup, do_undos, error;
  534 
  535         DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
  536             (u_long)nsops));
  537 
  538         semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
  539 
  540         if (semid < 0 || semid >= seminfo.semmni)
  541                 return (EINVAL);
  542 
  543         if ((semaptr = sema[semid]) == NULL ||
  544             semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
  545                 return (EINVAL);
  546 
  547         if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
  548                 DPRINTF(("error = %d from ipaccess\n", error));
  549                 return (error);
  550         }
  551 
  552         if (nsops == 0) {
  553                 *retval = 0;
  554                 return (0);
  555         } else if (nsops > (size_t)seminfo.semopm) {
  556                 DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
  557                     (u_long)nsops));
  558                 return (E2BIG);
  559         }
  560 
  561         if (nsops <= NSOPS)
  562                 sops = sopbuf;
  563         else
  564                 sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
  565         error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
  566         if (error != 0) {
  567                 DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
  568                     SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
  569                 goto done2;
  570         }
  571 
  572         /* 
  573          * Loop trying to satisfy the vector of requests.
  574          * If we reach a point where we must wait, any requests already
  575          * performed are rolled back and we go to sleep until some other
  576          * process wakes us up.  At this point, we start all over again.
  577          *
  578          * This ensures that from the perspective of other tasks, a set
  579          * of requests is atomic (never partially satisfied).
  580          */
  581         do_undos = 0;
  582 
  583         for (;;) {
  584                 do_wakeup = 0;
  585 
  586                 for (i = 0; i < nsops; i++) {
  587                         sopptr = &sops[i];
  588 
  589                         if (sopptr->sem_num >= semaptr->sem_nsems) {
  590                                 error = EFBIG;
  591                                 goto done2;
  592                         }
  593 
  594                         semptr = &semaptr->sem_base[sopptr->sem_num];
  595 
  596                         DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
  597                             semaptr, semaptr->sem_base, semptr,
  598                             sopptr->sem_num, semptr->semval, sopptr->sem_op,
  599                             (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
  600 
  601                         if (sopptr->sem_op < 0) {
  602                                 if ((int)(semptr->semval +
  603                                           sopptr->sem_op) < 0) {
  604                                         DPRINTF(("semop:  can't do it now\n"));
  605                                         break;
  606                                 } else {
  607                                         semptr->semval += sopptr->sem_op;
  608                                         if (semptr->semval == 0 &&
  609                                             semptr->semzcnt > 0)
  610                                                 do_wakeup = 1;
  611                                 }
  612                                 if (sopptr->sem_flg & SEM_UNDO)
  613                                         do_undos++;
  614                         } else if (sopptr->sem_op == 0) {
  615                                 if (semptr->semval > 0) {
  616                                         DPRINTF(("semop:  not zero now\n"));
  617                                         break;
  618                                 }
  619                         } else {
  620                                 if (semptr->semncnt > 0)
  621                                         do_wakeup = 1;
  622                                 semptr->semval += sopptr->sem_op;
  623                                 if (sopptr->sem_flg & SEM_UNDO)
  624                                         do_undos++;
  625                         }
  626                 }
  627 
  628                 /*
  629                  * Did we get through the entire vector and can we undo it?
  630                  */
  631                 if (i >= nsops && do_undos <= SEMUME)
  632                         goto done;
  633 
  634                 /*
  635                  * No ... rollback anything that we've already done
  636                  */
  637                 DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
  638                 for (j = 0; j < i; j++)
  639                         semaptr->sem_base[sops[j].sem_num].semval -=
  640                             sops[j].sem_op;
  641 
  642                 /*
  643                  * Did we have too many SEM_UNDO's
  644                  */
  645                 if (do_undos > SEMUME) {
  646                         error = ENOSPC;
  647                         goto done2;
  648                 }
  649 
  650                 /*
  651                  * If the request that we couldn't satisfy has the
  652                  * NOWAIT flag set then return with EAGAIN.
  653                  */
  654                 if (sopptr->sem_flg & IPC_NOWAIT) {
  655                         error = EAGAIN;
  656                         goto done2;
  657                 }
  658 
  659                 if (sopptr->sem_op == 0)
  660                         semptr->semzcnt++;
  661                 else
  662                         semptr->semncnt++;
  663 
  664                 DPRINTF(("semop:  good night!\n"));
  665                 error = tsleep_nsec(&sema[semid], PLOCK | PCATCH,
  666                     "semwait", INFSLP);
  667                 DPRINTF(("semop:  good morning (error=%d)!\n", error));
  668 
  669                 suptr = NULL;   /* sem_undo may have been reallocated */
  670 
  671                 /*
  672                  * Make sure that the semaphore still exists
  673                  */
  674                 if (sema[semid] == NULL ||
  675                     semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
  676                         error = EIDRM;
  677                         goto done2;
  678                 }
  679 
  680                 /*
  681                  * The semaphore is still alive.  Readjust the count of
  682                  * waiting processes.
  683                  */
  684                 if (sopptr->sem_op == 0)
  685                         semptr->semzcnt--;
  686                 else
  687                         semptr->semncnt--;
  688 
  689                 /*
  690                  * Is it really morning, or was our sleep interrupted?
  691                  * (Delayed check of tsleep() return code because we
  692                  * need to decrement sem[nz]cnt either way.)
  693                  */
  694                 if (error != 0) {
  695                         error = EINTR;
  696                         goto done2;
  697                 }
  698                 DPRINTF(("semop:  good morning!\n"));
  699         }
  700 
  701 done:
  702         /*
  703          * Process any SEM_UNDO requests.
  704          */
  705         if (do_undos) {
  706                 for (i = 0; i < nsops; i++) {
  707                         /*
  708                          * We only need to deal with SEM_UNDO's for non-zero
  709                          * op's.
  710                          */
  711                         int adjval;
  712 
  713                         if ((sops[i].sem_flg & SEM_UNDO) == 0)
  714                                 continue;
  715                         adjval = sops[i].sem_op;
  716                         if (adjval == 0)
  717                                 continue;
  718                         error = semundo_adjust(p, &suptr, semid,
  719                             sops[i].sem_num, -adjval);
  720                         if (error == 0)
  721                                 continue;
  722 
  723                         /*
  724                          * Uh-Oh!  We ran out of either sem_undo's or undo's.
  725                          * Rollback the adjustments to this point and then
  726                          * rollback the semaphore ups and down so we can return
  727                          * with an error with all structures restored.  We
  728                          * rollback the undo's in the exact reverse order that
  729                          * we applied them.  This guarantees that we won't run
  730                          * out of space as we roll things back out.
  731                          */
  732                         for (j = i; j > 0;) {
  733                                 j--;
  734                                 if ((sops[j].sem_flg & SEM_UNDO) == 0)
  735                                         continue;
  736                                 adjval = sops[j].sem_op;
  737                                 if (adjval == 0)
  738                                         continue;
  739                                 if (semundo_adjust(p, &suptr, semid,
  740                                     sops[j].sem_num, adjval) != 0)
  741                                         panic("semop - can't undo undos");
  742                         }
  743 
  744                         for (j = 0; j < nsops; j++)
  745                                 semaptr->sem_base[sops[j].sem_num].semval -=
  746                                     sops[j].sem_op;
  747 
  748                         DPRINTF(("error = %d from semundo_adjust\n", error));
  749                         goto done2;
  750                 } /* loop through the sops */
  751         } /* if (do_undos) */
  752 
  753         /* We're definitely done - set the sempid's */
  754         for (i = 0; i < nsops; i++) {
  755                 sopptr = &sops[i];
  756                 semptr = &semaptr->sem_base[sopptr->sem_num];
  757                 semptr->sempid = p->p_p->ps_pid;
  758         }
  759 
  760         semaptr->sem_otime = gettime();
  761 
  762         /* Do a wakeup if any semaphore was up'd. */
  763         if (do_wakeup) {
  764                 DPRINTF(("semop:  doing wakeup\n"));
  765                 wakeup(&sema[semid]);
  766                 DPRINTF(("semop:  back from wakeup\n"));
  767         }
  768         DPRINTF(("semop:  done\n"));
  769         *retval = 0;
  770 done2:
  771         if (sops != sopbuf)
  772                 free(sops, M_SEM, nsops * sizeof(struct sembuf));
  773         return (error);
  774 }
  775 
  776 /*
  777  * Go through the undo structures for this process and apply the adjustments to
  778  * semaphores.
  779  */
  780 void
  781 semexit(struct process *pr)
  782 {
  783         struct sem_undo *suptr;
  784         struct sem_undo **supptr;
  785 
  786         /*
  787          * Go through the chain of undo vectors looking for one associated with
  788          * this process.  Remember the pointer to the pointer to the element
  789          * to dequeue it later.
  790          */
  791         supptr = &SLIST_FIRST(&semu_list);
  792         SLIST_FOREACH(suptr, &semu_list, un_next) {
  793                 if (suptr->un_proc == pr)
  794                         break;
  795                 supptr = &SLIST_NEXT(suptr, un_next);
  796         }
  797 
  798         /*
  799          * If there is no undo vector, skip to the end.
  800          */
  801         if (suptr == NULL)
  802                 return;
  803 
  804         /*
  805          * We now have an undo vector for this process.
  806          */
  807         DPRINTF(("process @%p has undo structure with %d entries\n", pr,
  808             suptr->un_cnt));
  809 
  810         /*
  811          * If there are any active undo elements then process them.
  812          */
  813         if (suptr->un_cnt > 0) {
  814                 int ix;
  815 
  816                 for (ix = 0; ix < suptr->un_cnt; ix++) {
  817                         int semid = suptr->un_ent[ix].un_id;
  818                         int semnum = suptr->un_ent[ix].un_num;
  819                         int adjval = suptr->un_ent[ix].un_adjval;
  820                         struct semid_ds *semaptr;
  821 
  822                         if ((semaptr = sema[semid]) == NULL)
  823                                 panic("semexit - semid not allocated");
  824                         if (semnum >= semaptr->sem_nsems)
  825                                 panic("semexit - semnum out of range");
  826 
  827                         DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
  828                             suptr->un_proc, suptr->un_ent[ix].un_id,
  829                             suptr->un_ent[ix].un_num,
  830                             suptr->un_ent[ix].un_adjval,
  831                             semaptr->sem_base[semnum].semval));
  832 
  833                         if (adjval < 0 &&
  834                             semaptr->sem_base[semnum].semval < -adjval)
  835                                 semaptr->sem_base[semnum].semval = 0;
  836                         else
  837                                 semaptr->sem_base[semnum].semval += adjval;
  838 
  839                         wakeup(&sema[semid]);
  840                         DPRINTF(("semexit:  back from wakeup\n"));
  841                 }
  842         }
  843 
  844         /*
  845          * Deallocate the undo vector.
  846          */
  847         DPRINTF(("removing vector\n"));
  848         *supptr = SLIST_NEXT(suptr, un_next);
  849         pool_put(&semu_pool, suptr);
  850         semutot--;
  851 }
  852 
  853 /* Expand semsegs and semseqs arrays */
  854 void
  855 sema_reallocate(int val)
  856 {
  857         struct semid_ds **sema_new;
  858         unsigned short *newseqs;
  859         sema_new = mallocarray(val, sizeof(struct semid_ds *),
  860             M_SEM, M_WAITOK|M_ZERO);
  861         memcpy(sema_new, sema,
  862             seminfo.semmni * sizeof(struct semid_ds *));
  863         newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
  864             M_WAITOK|M_ZERO);
  865         memcpy(newseqs, semseqs,
  866             seminfo.semmni * sizeof(unsigned short));
  867         free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *));
  868         free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short));
  869         sema = sema_new;
  870         semseqs = newseqs;
  871         seminfo.semmni = val;
  872 }
  873 
  874 const struct sysctl_bounded_args sysvsem_vars[] = {
  875         { KERN_SEMINFO_SEMUME, &seminfo.semume, SYSCTL_INT_READONLY },
  876         { KERN_SEMINFO_SEMUSZ, &seminfo.semusz, SYSCTL_INT_READONLY },
  877         { KERN_SEMINFO_SEMVMX, &seminfo.semvmx, SYSCTL_INT_READONLY },
  878         { KERN_SEMINFO_SEMAEM, &seminfo.semaem, SYSCTL_INT_READONLY },
  879         { KERN_SEMINFO_SEMOPM, &seminfo.semopm, 1, INT_MAX },
  880 };
  881 
  882 /*
  883  * Userland access to struct seminfo.
  884  */
  885 int
  886 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
  887         void *newp, size_t newlen)
  888 {
  889         int error, val;
  890 
  891         if (namelen != 1)
  892                         return (ENOTDIR);       /* leaf-only */
  893 
  894         switch (name[0]) {
  895         case KERN_SEMINFO_SEMMNI:
  896                 val = seminfo.semmni;
  897                 error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
  898                     &val, val, 0xffff);
  899                 /* returns success and skips reallocation if val is unchanged */
  900                 if (error || val == seminfo.semmni)
  901                         return (error);
  902                 sema_reallocate(val);
  903                 return (0);
  904         case KERN_SEMINFO_SEMMNS:
  905                 /* can't decrease semmns or go over 2^16 */
  906                 return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
  907                     &seminfo.semmns, seminfo.semmns, 0xffff));
  908         case KERN_SEMINFO_SEMMNU:
  909                 /* can't decrease semmnu or go over 2^16 */
  910                 return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
  911                     &seminfo.semmnu, seminfo.semmnu, 0xffff));
  912         case KERN_SEMINFO_SEMMSL:
  913                 /* can't decrease semmsl or go over 2^16 */
  914                 return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
  915                     &seminfo.semmsl, seminfo.semmsl, 0xffff));
  916         default:
  917                 return (sysctl_bounded_arr(sysvsem_vars, nitems(sysvsem_vars),
  918                     name, namelen, oldp, oldlenp, newp, newlen));
  919         }
  920         /* NOTREACHED */
  921 }

Cache object: 09adf32552587b74037cf546dc2d97af


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