root/kern/sysv_sem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SLIST_HEAD
  2. semu_alloc
  3. semundo_adjust
  4. semundo_clear
  5. sys___semctl
  6. semctl1
  7. sys_semget
  8. sys_semop
  9. semexit
  10. sysctl_sysvsem

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

/* [<][>][^][v][top][bottom][index][help] */