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/bsd/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 /*
    2  * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
    3  *
    4  * @APPLE_LICENSE_HEADER_START@
    5  * 
    6  * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
    7  * 
    8  * This file contains Original Code and/or Modifications of Original Code
    9  * as defined in and that are subject to the Apple Public Source License
   10  * Version 2.0 (the 'License'). You may not use this file except in
   11  * compliance with the License. Please obtain a copy of the License at
   12  * http://www.opensource.apple.com/apsl/ and read it before using this
   13  * file.
   14  * 
   15  * The Original Code and all software distributed under the License are
   16  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   19  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
   20  * Please see the License for the specific language governing rights and
   21  * limitations under the License.
   22  * 
   23  * @APPLE_LICENSE_HEADER_END@
   24  */
   25 /*
   26  * Implementation of SVID semaphores
   27  *
   28  * Author:  Daniel Boulet
   29  *
   30  * This software is provided ``AS IS'' without any warranties of any kind.
   31  */
   32 /*
   33  * John Bellardo modified the implementation for Darwin. 12/2000
   34  */
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/kernel.h>
   39 #include <sys/proc.h>
   40 #include <sys/sem.h>
   41 #include <sys/malloc.h>
   42 #include <mach/mach_types.h>
   43 
   44 #include <sys/filedesc.h>
   45 #include <sys/file.h>
   46 #include <sys/kern_audit.h>
   47 #include <sys/sysctl.h>
   48 
   49 /*#include <sys/sysproto.h>*/
   50 /*#include <sys/sysent.h>*/
   51 
   52 /* Uncomment this line to see the debugging output */
   53 /* #define SEM_DEBUG */
   54 
   55 /* Macros to deal with the semaphore subsystem lock.  The lock currently uses
   56  * the semlock_holder static variable as a mutex.  NULL means no lock, any
   57  * value other than NULL means locked.  semlock_holder is used because it was
   58  * present in the code before the Darwin port, and for no other reason.
   59  * When the time comes to relax the funnel requirements of the kernel only
   60  * these macros should need to be changed.  A spin lock would work well.
   61  */
   62 /* Aquire the lock */
   63 #define SUBSYSTEM_LOCK_AQUIRE(p) { sysv_sem_aquiring_threads++; \
   64     while (semlock_holder != NULL) \
   65         (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "sysvsem", 0); \
   66     semlock_holder = p; \
   67     sysv_sem_aquiring_threads--; }
   68 
   69 /* Release the lock */
   70 #define SUBSYSTEM_LOCK_RELEASE { semlock_holder = NULL; wakeup((caddr_t)&semlock_holder); }
   71 
   72 /* Release the lock and return a value */
   73 #define UNLOCK_AND_RETURN(ret) { SUBSYSTEM_LOCK_RELEASE; return(ret); }
   74 
   75 #define M_SYSVSEM       M_SUBPROC
   76 
   77 #if 0
   78 static void seminit __P((void *));
   79 SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL)
   80 #endif 0
   81 
   82 /* Hard system limits to avoid resource starvation / DOS attacks.
   83  * These are not needed if we can make the semaphore pages swappable.
   84  */
   85 static struct seminfo limitseminfo = {
   86         SEMMAP,        /* # of entries in semaphore map */
   87         SEMMNI,        /* # of semaphore identifiers */
   88         SEMMNS,        /* # of semaphores in system */
   89         SEMMNU,        /* # of undo structures in system */
   90         SEMMSL,        /* max # of semaphores per id */
   91         SEMOPM,        /* max # of operations per semop call */
   92         SEMUME,        /* max # of undo entries per process */
   93         SEMUSZ,        /* size in bytes of undo structure */
   94         SEMVMX,        /* semaphore maximum value */
   95         SEMAEM         /* adjust on exit max value */
   96 };
   97 
   98 /* Current system allocations.  We use this structure to track how many
   99  * resources we have allocated so far.  This way we can set large hard limits
  100  * and not allocate the memory for them up front.
  101  */
  102 struct seminfo seminfo = {
  103         SEMMAP, /* Unused, # of entries in semaphore map */
  104         0,      /* # of semaphore identifiers */
  105         0,      /* # of semaphores in system */
  106         0,      /* # of undo entries in system */
  107         SEMMSL, /* max # of semaphores per id */
  108         SEMOPM, /* max # of operations per semop call */
  109         SEMUME, /* max # of undo entries per process */
  110         SEMUSZ, /* size in bytes of undo structure */
  111         SEMVMX, /* semaphore maximum value */
  112         SEMAEM  /* adjust on exit max value */
  113 };
  114 
  115 /* A counter so the module unload code knows when there are no more processes using
  116  * the sysv_sem code */
  117 static long sysv_sem_sleeping_threads = 0;
  118 static long sysv_sem_aquiring_threads = 0;
  119 
  120 struct semctl_args;
  121 int semctl __P((struct proc *p, struct semctl_args *uap, int *));
  122 struct semget_args;
  123 int semget __P((struct proc *p, struct semget_args *uap, int *));
  124 struct semop_args;
  125 int semop __P((struct proc *p, struct semop_args *uap, int *));
  126 struct semconfig_args;
  127 int semconfig __P((struct proc *p, struct semconfig_args *uap, int *));
  128 
  129 
  130 static struct sem_undo *semu_alloc __P((struct proc *p));
  131 static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr, 
  132                 int semid, int semnum, int adjval));
  133 static void semundo_clear __P((int semid, int semnum));
  134 
  135 typedef int     sy_call_t __P((struct proc *, void *, int *));
  136 
  137 /* XXX casting to (sy_call_t *) is bogus, as usual. */
  138 static sy_call_t *semcalls[] = {
  139         (sy_call_t *)semctl, (sy_call_t *)semget,
  140         (sy_call_t *)semop, (sy_call_t *)semconfig
  141 };
  142 
  143 static int      semtot = 0;                     /* # of used semaphores */
  144 struct semid_ds *sema = NULL;                   /* semaphore id pool */
  145 struct sem *sem =  NULL;                        /* semaphore pool */
  146 static struct sem_undo *semu_list = NULL;   /* list of active undo structures */
  147 struct sem_undo *semu = NULL;                   /* semaphore undo pool */
  148 
  149 static struct proc *semlock_holder = NULL;
  150 
  151 /* seminit no longer needed.  The data structures are grown dynamically */
  152 void
  153 seminit()
  154 {
  155 }
  156 
  157 /*
  158  * Entry point for all SEM calls
  159  *
  160  * In Darwin this is no longer the entry point.  It will be removed after
  161  *  the code has been tested better.
  162  */
  163 struct semsys_args {
  164         u_int   which;
  165         int     a2;
  166         int     a3;
  167         int     a4;
  168         int     a5;
  169 };
  170 int
  171 semsys(p, uap, retval)
  172         struct proc *p;
  173         /* XXX actually varargs. */
  174         struct semsys_args *uap;
  175         register_t *retval;
  176 {
  177 
  178         /* The individual calls handling the locking now */
  179         /*while (semlock_holder != NULL && semlock_holder != p)
  180                 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
  181          */
  182 
  183         if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
  184                 return (EINVAL);
  185         return ((*semcalls[uap->which])(p, &uap->a2, retval));
  186 }
  187 
  188 /*
  189  * Lock or unlock the entire semaphore facility.
  190  *
  191  * This will probably eventually evolve into a general purpose semaphore
  192  * facility status enquiry mechanism (I don't like the "read /dev/kmem"
  193  * approach currently taken by ipcs and the amount of info that we want
  194  * to be able to extract for ipcs is probably beyond what the capability
  195  * of the getkerninfo facility.
  196  *
  197  * At the time that the current version of semconfig was written, ipcs is
  198  * the only user of the semconfig facility.  It uses it to ensure that the
  199  * semaphore facility data structures remain static while it fishes around
  200  * in /dev/kmem.
  201  */
  202 
  203 #ifndef _SYS_SYSPROTO_H_
  204 struct semconfig_args {
  205         semconfig_ctl_t flag;
  206 };
  207 #endif
  208 
  209 int
  210 semconfig(p, uap, retval)
  211         struct proc *p;
  212         struct semconfig_args *uap;
  213         register_t *retval;
  214 {
  215         int eval = 0;
  216 
  217         switch (uap->flag) {
  218         case SEM_CONFIG_FREEZE:
  219                 SUBSYSTEM_LOCK_AQUIRE(p);
  220                 break;
  221 
  222         case SEM_CONFIG_THAW:
  223                 SUBSYSTEM_LOCK_RELEASE;
  224                 break;
  225 
  226         default:
  227                 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
  228                     uap->flag);
  229                 eval = EINVAL;
  230                 break;
  231         }
  232 
  233         *retval = 0;
  234         return(eval);
  235 }
  236 
  237 /* Expand the semu array to the given capacity.  If the expansion fails
  238  * return 0, otherwise return 1.
  239  *
  240  * Assumes we already have the subsystem lock.
  241  */
  242 static int
  243 grow_semu_array(newSize)
  244         int newSize;
  245 {
  246         register int i, j;
  247         register struct sem_undo *newSemu;
  248         if (newSize <= seminfo.semmnu)
  249                 return 0;
  250         if (newSize > limitseminfo.semmnu) /* enforce hard limit */
  251         {
  252 #ifdef SEM_DEBUG
  253                 printf("undo structure hard limit of %d reached, requested %d\n",
  254                         limitseminfo.semmnu, newSize);
  255 #endif
  256                 return 0;
  257         }
  258         newSize = (newSize/SEMMNU_INC + 1) * SEMMNU_INC;
  259         newSize = newSize > limitseminfo.semmnu ? limitseminfo.semmnu : newSize;
  260 
  261 #ifdef SEM_DEBUG
  262         printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize);
  263 #endif
  264         MALLOC(newSemu, struct sem_undo*, sizeof(struct sem_undo)*newSize,
  265                 M_SYSVSEM, M_WAITOK);
  266         if (NULL == newSemu)
  267         {
  268 #ifdef SEM_DEBUG
  269                 printf("allocation failed.  no changes made.\n");
  270 #endif
  271                 return 0;
  272         }
  273 
  274         /* Initialize our structure.  */
  275         for (i = 0; i < seminfo.semmnu; i++)
  276         {
  277                 newSemu[i] = semu[i];
  278                 for(j = 0; j < SEMUME; j++)   /* Is this really needed? */
  279                         newSemu[i].un_ent[j] = semu[i].un_ent[j];
  280         }
  281         for (i = seminfo.semmnu; i < newSize; i++)
  282         {
  283                 newSemu[i].un_proc = NULL;
  284         }
  285 
  286         /* Clean up the old array */
  287         if (semu)
  288                 FREE(semu, M_SYSVSEM);
  289 
  290         semu = newSemu;
  291         seminfo.semmnu = newSize;
  292 #ifdef SEM_DEBUG
  293         printf("expansion successful\n");
  294 #endif
  295         return 1;
  296 }
  297 
  298 /*
  299  * Expand the sema array to the given capacity.  If the expansion fails
  300  * we return 0, otherwise we return 1.
  301  *
  302  * Assumes we already have the subsystem lock.
  303  */
  304 static int
  305 grow_sema_array(newSize)
  306         int newSize;
  307 {
  308         register struct semid_ds *newSema;
  309         register int i;
  310 
  311         if (newSize <= seminfo.semmni)
  312                 return 0;
  313         if (newSize > limitseminfo.semmni) /* enforce hard limit */
  314         {
  315 #ifdef SEM_DEBUG
  316                 printf("identifier hard limit of %d reached, requested %d\n",
  317                         limitseminfo.semmni, newSize);
  318 #endif
  319                 return 0;
  320         }
  321         newSize = (newSize/SEMMNI_INC + 1) * SEMMNI_INC;
  322         newSize = newSize > limitseminfo.semmni ? limitseminfo.semmni : newSize;
  323 
  324 #ifdef SEM_DEBUG
  325         printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize);
  326 #endif
  327         MALLOC(newSema, struct semid_ds*, sizeof(struct semid_ds)*newSize,
  328                 M_SYSVSEM, M_WAITOK);
  329         if (NULL == newSema)
  330         {
  331 #ifdef SEM_DEBUG
  332                 printf("allocation failed.  no changes made.\n");
  333 #endif
  334                 return 0;
  335         }
  336 
  337         /* Initialize our new ids, and copy over the old ones */
  338         for (i = 0; i < seminfo.semmni; i++)
  339         {
  340                 newSema[i] = sema[i];
  341                 /* This is a hack.  What we really want to be able to
  342                  * do is change the value a process is waiting on
  343                  * without waking it up, but I don't know how to do
  344                  * this with the existing code, so we wake up the
  345                  * process and let it do a lot of work to determine the
  346                  * semaphore set is really not available yet, and then
  347                  * sleep on the correct, reallocated semid_ds pointer.
  348                  */
  349                 if (sema[i].sem_perm.mode & SEM_ALLOC)
  350                         wakeup((caddr_t)&sema[i]);
  351         }
  352 
  353         for (i = seminfo.semmni; i < newSize; i++)
  354         {
  355                 newSema[i].sem_base = 0;
  356                 newSema[i].sem_perm.mode = 0;
  357         }
  358 
  359         /* Clean up the old array */
  360         if (sema)
  361                 FREE(sema, M_SYSVSEM);
  362 
  363         sema = newSema;
  364         seminfo.semmni = newSize;
  365 #ifdef SEM_DEBUG
  366         printf("expansion successful\n");
  367 #endif
  368         return 1;
  369 }
  370 
  371 /*
  372  * Expand the sem array to the given capacity.  If the expansion fails
  373  * we return 0 (fail), otherwise we return 1 (success).
  374  *
  375  * Assumes we already hold the subsystem lock.
  376  */
  377 static int
  378 grow_sem_array(newSize)
  379                 int newSize;
  380 {
  381         register struct sem *newSem = NULL;
  382         register int i;
  383 
  384         if (newSize < semtot)
  385                 return 0;
  386         if (newSize > limitseminfo.semmns) /* enforce hard limit */
  387         {
  388 #ifdef SEM_DEBUG
  389                 printf("semaphore hard limit of %d reached, requested %d\n",
  390                         limitseminfo.semmns, newSize);
  391 #endif
  392                 return 0;
  393         }
  394         newSize = (newSize/SEMMNS_INC + 1) * SEMMNS_INC;
  395         newSize = newSize > limitseminfo.semmns ? limitseminfo.semmns : newSize;
  396 
  397 #ifdef SEM_DEBUG
  398         printf("growing sem array from %d to %d\n", seminfo.semmns, newSize);
  399 #endif
  400         MALLOC(newSem, struct sem*, sizeof(struct sem)*newSize,
  401                 M_SYSVSEM, M_WAITOK);
  402         if (NULL == newSem)
  403         {
  404 #ifdef SEM_DEBUG
  405                 printf("allocation failed.  no changes made.\n");
  406 #endif
  407                 return 0;
  408         }
  409 
  410         /* We have our new memory, now copy the old contents over */
  411         if (sem)
  412                 for(i = 0; i < seminfo.semmns; i++)
  413                         newSem[i] = sem[i];
  414 
  415         /* Update our id structures to point to the new semaphores */
  416         for(i = 0; i < seminfo.semmni; i++)
  417                 if (sema[i].sem_perm.mode & SEM_ALLOC)  /* ID in use */
  418                 {
  419                         if (newSem > sem)
  420                                 sema[i].sem_base += newSem - sem;
  421                         else
  422                                 sema[i].sem_base -= sem - newSem;
  423                 }
  424 
  425         /* clean up the old array */
  426         if (sem)
  427                 FREE(sem, M_SYSVSEM);
  428 
  429         sem = newSem;
  430         seminfo.semmns = newSize;
  431 #ifdef SEM_DEBUG
  432         printf("expansion complete\n");
  433 #endif
  434         return 1;
  435 }
  436 
  437 /*
  438  * Allocate a new sem_undo structure for a process
  439  * (returns ptr to structure or NULL if no more room)
  440  *
  441  * Assumes we already hold the subsystem lock.
  442  */
  443 
  444 static struct sem_undo *
  445 semu_alloc(p)
  446         struct proc *p;
  447 {
  448         register int i;
  449         register struct sem_undo *suptr;
  450         register struct sem_undo **supptr;
  451         int attempt;
  452 
  453         /*
  454          * Try twice to allocate something.
  455          * (we'll purge any empty structures after the first pass so
  456          * two passes are always enough)
  457          */
  458 
  459         for (attempt = 0; attempt < 2; attempt++) {
  460                 /*
  461                  * Look for a free structure.
  462                  * Fill it in and return it if we find one.
  463                  */
  464 
  465                 for (i = 0; i < seminfo.semmnu; i++) {
  466                         suptr = SEMU(i);
  467                         if (suptr->un_proc == NULL) {
  468                                 suptr->un_next = semu_list;
  469                                 semu_list = suptr;
  470                                 suptr->un_cnt = 0;
  471                                 suptr->un_proc = p;
  472                                 return(suptr);
  473                         }
  474                 }
  475 
  476                 /*
  477                  * We didn't find a free one, if this is the first attempt
  478                  * then try to free some structures.
  479                  */
  480 
  481                 if (attempt == 0) {
  482                         /* All the structures are in use - try to free some */
  483                         int did_something = 0;
  484 
  485                         supptr = &semu_list;
  486                         while ((suptr = *supptr) != NULL) {
  487                                 if (suptr->un_cnt == 0)  {
  488                                         suptr->un_proc = NULL;
  489                                         *supptr = suptr->un_next;
  490                                         did_something = 1;
  491                                 } else
  492                                         supptr = &(suptr->un_next);
  493                         }
  494 
  495                         /* If we didn't free anything. Try expanding
  496                          * the semu[] array.  If that doesn't work
  497                          * then fail.  We expand last to get the
  498                          * most reuse out of existing resources.
  499                          */
  500                         if (!did_something)
  501                                 if (!grow_semu_array(seminfo.semmnu + 1))
  502                                         return(NULL);
  503                 } else {
  504                         /*
  505                          * The second pass failed even though we freed
  506                          * something after the first pass!
  507                          * This is IMPOSSIBLE!
  508                          */
  509                         panic("semu_alloc - second attempt failed");
  510                 }
  511         }
  512         return (NULL);
  513 }
  514 
  515 /*
  516  * Adjust a particular entry for a particular proc
  517  *
  518  * Assumes we already hold the subsystem lock.
  519  */
  520 
  521 static int
  522 semundo_adjust(p, supptr, semid, semnum, adjval)
  523         register struct proc *p;
  524         struct sem_undo **supptr;
  525         int semid, semnum;
  526         int adjval;
  527 {
  528         register struct sem_undo *suptr;
  529         register struct undo *sunptr;
  530         int i;
  531 
  532         /* Look for and remember the sem_undo if the caller doesn't provide
  533            it */
  534 
  535         suptr = *supptr;
  536         if (suptr == NULL) {
  537                 for (suptr = semu_list; suptr != NULL;
  538                     suptr = suptr->un_next) {
  539                         if (suptr->un_proc == p) {
  540                                 *supptr = suptr;
  541                                 break;
  542                         }
  543                 }
  544                 if (suptr == NULL) {
  545                         if (adjval == 0)
  546                                 return(0);
  547                         suptr = semu_alloc(p);
  548                         if (suptr == NULL)
  549                                 return(ENOSPC);
  550                         *supptr = suptr;
  551                 }
  552         }
  553 
  554         /*
  555          * Look for the requested entry and adjust it (delete if adjval becomes
  556          * 0).
  557          */
  558         sunptr = &suptr->un_ent[0];
  559         for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
  560                 if (sunptr->un_id != semid || sunptr->un_num != semnum)
  561                         continue;
  562                 if (adjval == 0)
  563                         sunptr->un_adjval = 0;
  564                 else
  565                         sunptr->un_adjval += adjval;
  566                 if (sunptr->un_adjval == 0) {
  567                         suptr->un_cnt--;
  568                         if (i < suptr->un_cnt)
  569                                 suptr->un_ent[i] =
  570                                     suptr->un_ent[suptr->un_cnt];
  571                 }
  572                 return(0);
  573         }
  574 
  575         /* Didn't find the right entry - create it */
  576         if (adjval == 0)
  577                 return(0);
  578         if (suptr->un_cnt != limitseminfo.semume) {
  579                 sunptr = &suptr->un_ent[suptr->un_cnt];
  580                 suptr->un_cnt++;
  581                 sunptr->un_adjval = adjval;
  582                 sunptr->un_id = semid; sunptr->un_num = semnum;
  583         } else
  584                 return(EINVAL);
  585         return(0);
  586 }
  587 
  588 /* Assumes we already hold the subsystem lock.
  589  */
  590 static void
  591 semundo_clear(semid, semnum)
  592         int semid, semnum;
  593 {
  594         register struct sem_undo *suptr;
  595 
  596         for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
  597                 register struct undo *sunptr = &suptr->un_ent[0];
  598                 register int i = 0;
  599 
  600                 while (i < suptr->un_cnt) {
  601                         if (sunptr->un_id == semid) {
  602                                 if (semnum == -1 || sunptr->un_num == semnum) {
  603                                         suptr->un_cnt--;
  604                                         if (i < suptr->un_cnt) {
  605                                                 suptr->un_ent[i] =
  606                                                   suptr->un_ent[suptr->un_cnt];
  607                                                 continue;
  608                                         }
  609                                 }
  610                                 if (semnum != -1)
  611                                         break;
  612                         }
  613                         i++, sunptr++;
  614                 }
  615         }
  616 }
  617 
  618 /*
  619  * Note that the user-mode half of this passes a union, not a pointer
  620  */
  621 #ifndef _SYS_SYSPROTO_H_
  622 struct semctl_args {
  623         int     semid;
  624         int     semnum;
  625         int     cmd;
  626         union   semun arg;
  627 };
  628 #endif
  629 
  630 int
  631 semctl(p, uap, retval)
  632         struct proc *p;
  633         register struct semctl_args *uap;
  634         register_t *retval;
  635 {
  636         int semid = uap->semid;
  637         int semnum = uap->semnum;
  638         int cmd = uap->cmd;
  639         union semun arg = uap->arg;
  640         union semun real_arg;
  641         struct ucred *cred = p->p_ucred;
  642         int i, rval, eval;
  643         struct semid_ds sbuf;
  644         register struct semid_ds *semaptr;
  645 
  646         AUDIT_ARG(svipc_cmd, cmd);
  647         AUDIT_ARG(svipc_id, semid);
  648         SUBSYSTEM_LOCK_AQUIRE(p);
  649 #ifdef SEM_DEBUG
  650         printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
  651 #endif
  652 
  653         semid = IPCID_TO_IX(semid);
  654         if (semid < 0 || semid >= seminfo.semmni)
  655 {
  656 #ifdef SEM_DEBUG
  657                 printf("Invalid semid\n");
  658 #endif
  659                 UNLOCK_AND_RETURN(EINVAL);
  660 }
  661 
  662         semaptr = &sema[semid];
  663         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
  664             semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
  665                 UNLOCK_AND_RETURN(EINVAL);
  666 
  667         eval = 0;
  668         rval = 0;
  669 
  670         switch (cmd) {
  671         case IPC_RMID:
  672                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
  673                         UNLOCK_AND_RETURN(eval);
  674                 semaptr->sem_perm.cuid = cred->cr_uid;
  675                 semaptr->sem_perm.uid = cred->cr_uid;
  676                 semtot -= semaptr->sem_nsems;
  677                 for (i = semaptr->sem_base - sem; i < semtot; i++)
  678                         sem[i] = sem[i + semaptr->sem_nsems];
  679                 for (i = 0; i < seminfo.semmni; i++) {
  680                         if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
  681                             sema[i].sem_base > semaptr->sem_base)
  682                                 sema[i].sem_base -= semaptr->sem_nsems;
  683                 }
  684                 semaptr->sem_perm.mode = 0;
  685                 semundo_clear(semid, -1);
  686                 wakeup((caddr_t)semaptr);
  687                 break;
  688 
  689         case IPC_SET:
  690                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
  691                         UNLOCK_AND_RETURN(eval);
  692                 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
  693                         UNLOCK_AND_RETURN(eval);*/
  694                 if ((eval = copyin(arg.buf, (caddr_t)&sbuf,
  695                     sizeof(sbuf))) != 0)
  696                         UNLOCK_AND_RETURN(eval);
  697                 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
  698                 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
  699                 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
  700                     (sbuf.sem_perm.mode & 0777);
  701                 semaptr->sem_ctime = time_second;
  702                 break;
  703 
  704         case IPC_STAT:
  705                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  706                         UNLOCK_AND_RETURN(eval);
  707                 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
  708                         UNLOCK_AND_RETURN(eval);*/
  709                 eval = copyout((caddr_t)semaptr, arg.buf,
  710                     sizeof(struct semid_ds));
  711                 break;
  712 
  713         case GETNCNT:
  714                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  715                         UNLOCK_AND_RETURN(eval);
  716                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  717                         UNLOCK_AND_RETURN(EINVAL);
  718                 rval = semaptr->sem_base[semnum].semncnt;
  719                 break;
  720 
  721         case GETPID:
  722                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  723                         UNLOCK_AND_RETURN(eval);
  724                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  725                         UNLOCK_AND_RETURN(EINVAL);
  726                 rval = semaptr->sem_base[semnum].sempid;
  727                 break;
  728 
  729         case GETVAL:
  730                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  731                         UNLOCK_AND_RETURN(eval);
  732                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  733                         UNLOCK_AND_RETURN(EINVAL);
  734                 rval = semaptr->sem_base[semnum].semval;
  735                 break;
  736 
  737         case GETALL:
  738                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  739                         UNLOCK_AND_RETURN(eval);
  740                 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
  741                         UNLOCK_AND_RETURN(eval);*/
  742                 for (i = 0; i < semaptr->sem_nsems; i++) {
  743                         eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
  744                             &arg.array[i], sizeof(arg.array[0]));
  745                         if (eval != 0)
  746                                 break;
  747                 }
  748                 break;
  749 
  750         case GETZCNT:
  751                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
  752                         UNLOCK_AND_RETURN(eval);
  753                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  754                         UNLOCK_AND_RETURN(EINVAL);
  755                 rval = semaptr->sem_base[semnum].semzcnt;
  756                 break;
  757 
  758         case SETVAL:
  759                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  760                 {
  761 #ifdef SEM_DEBUG
  762                         printf("Invalid credentials for write\n");
  763 #endif
  764                         UNLOCK_AND_RETURN(eval);
  765                 }
  766                 if (semnum < 0 || semnum >= semaptr->sem_nsems)
  767                 {
  768 #ifdef SEM_DEBUG
  769                         printf("Invalid number out of range for set\n");
  770 #endif
  771                         UNLOCK_AND_RETURN(EINVAL);
  772                 }
  773                 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
  774                 {
  775 #ifdef SEM_DEBUG
  776                         printf("Error during value copyin\n");
  777 #endif
  778                         UNLOCK_AND_RETURN(eval);
  779                 }*/
  780                 semaptr->sem_base[semnum].semval = arg.val;
  781                 semundo_clear(semid, semnum);
  782                 wakeup((caddr_t)semaptr);
  783                 break;
  784 
  785         case SETALL:
  786                 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
  787                         UNLOCK_AND_RETURN(eval);
  788                 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
  789                         UNLOCK_AND_RETURN(eval);*/
  790                 for (i = 0; i < semaptr->sem_nsems; i++) {
  791                         eval = copyin(&arg.array[i],
  792                             (caddr_t)&semaptr->sem_base[i].semval,
  793                             sizeof(arg.array[0]));
  794                         if (eval != 0)
  795                                 break;
  796                 }
  797                 semundo_clear(semid, -1);
  798                 wakeup((caddr_t)semaptr);
  799                 break;
  800 
  801         default:
  802                 UNLOCK_AND_RETURN(EINVAL);
  803         }
  804 
  805         if (eval == 0)
  806                 *retval = rval;
  807         UNLOCK_AND_RETURN(eval);
  808 }
  809 
  810 #ifndef _SYS_SYSPROTO_H_
  811 struct semget_args {
  812         key_t   key;
  813         int     nsems;
  814         int     semflg;
  815 };
  816 #endif
  817 
  818 int
  819 semget(p, uap, retval)
  820         struct proc *p;
  821         register struct semget_args *uap;
  822         register_t *retval;
  823 {
  824         int semid, eval;
  825         int key = uap->key;
  826         int nsems = uap->nsems;
  827         int semflg = uap->semflg;
  828         struct ucred *cred = p->p_ucred;
  829 
  830         SUBSYSTEM_LOCK_AQUIRE(p);
  831 #ifdef SEM_DEBUG
  832         if (key != IPC_PRIVATE)
  833                 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
  834         else
  835                 printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems, semflg);
  836 #endif
  837     
  838         if (key != IPC_PRIVATE) {
  839                 for (semid = 0; semid < seminfo.semmni; semid++) {
  840                         if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
  841                             sema[semid].sem_perm.key == key)
  842                                 break;
  843                 }
  844                 if (semid < seminfo.semmni) {
  845 #ifdef SEM_DEBUG
  846                         printf("found public key\n");
  847 #endif
  848                         if ((eval = ipcperm(cred, &sema[semid].sem_perm,
  849                             semflg & 0700)))
  850                                 UNLOCK_AND_RETURN(eval);
  851                         if (nsems > 0 && sema[semid].sem_nsems < nsems) {
  852 #ifdef SEM_DEBUG
  853                                 printf("too small\n");
  854 #endif
  855                                 UNLOCK_AND_RETURN(EINVAL);
  856                         }
  857                         if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
  858 #ifdef SEM_DEBUG
  859                                 printf("not exclusive\n");
  860 #endif
  861                                 UNLOCK_AND_RETURN(EEXIST);
  862                         }
  863                         goto found;
  864                 }
  865         }
  866 
  867 #ifdef SEM_DEBUG
  868         printf("need to allocate an id for the request\n");
  869 #endif
  870         if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
  871                 if (nsems <= 0 || nsems > limitseminfo.semmsl) {
  872 #ifdef SEM_DEBUG
  873                         printf("nsems out of range (0<%d<=%d)\n", nsems,
  874                             seminfo.semmsl);
  875 #endif
  876                         UNLOCK_AND_RETURN(EINVAL);
  877                 }
  878                 if (nsems > seminfo.semmns - semtot) {
  879 #ifdef SEM_DEBUG
  880                         printf("not enough semaphores left (need %d, got %d)\n",
  881                             nsems, seminfo.semmns - semtot);
  882 #endif
  883                         if (!grow_sem_array(semtot + nsems))
  884                         {
  885 #ifdef SEM_DEBUG
  886                                 printf("failed to grow the sem array\n");
  887 #endif
  888                                 UNLOCK_AND_RETURN(ENOSPC);
  889                         }
  890                 }
  891                 for (semid = 0; semid < seminfo.semmni; semid++) {
  892                         if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
  893                                 break;
  894                 }
  895                 if (semid == seminfo.semmni) {
  896 #ifdef SEM_DEBUG
  897                         printf("no more id's available\n");
  898 #endif
  899                         if (!grow_sema_array(seminfo.semmni + 1))
  900                         {
  901 #ifdef SEM_DEBUG
  902                                 printf("failed to grow sema array\n");
  903 #endif
  904                                 UNLOCK_AND_RETURN(ENOSPC);
  905                         }
  906                 }
  907 #ifdef SEM_DEBUG
  908                 printf("semid %d is available\n", semid);
  909 #endif
  910                 sema[semid].sem_perm.key = key;
  911                 sema[semid].sem_perm.cuid = cred->cr_uid;
  912                 sema[semid].sem_perm.uid = cred->cr_uid;
  913                 sema[semid].sem_perm.cgid = cred->cr_gid;
  914                 sema[semid].sem_perm.gid = cred->cr_gid;
  915                 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
  916                 sema[semid].sem_perm.seq =
  917                     (sema[semid].sem_perm.seq + 1) & 0x7fff;
  918                 sema[semid].sem_nsems = nsems;
  919                 sema[semid].sem_otime = 0;
  920                 sema[semid].sem_ctime = time_second;
  921                 sema[semid].sem_base = &sem[semtot];
  922                 semtot += nsems;
  923                 bzero(sema[semid].sem_base,
  924                     sizeof(sema[semid].sem_base[0])*nsems);
  925 #ifdef SEM_DEBUG
  926                 printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
  927                     &sem[semtot]);
  928 #endif
  929         } else {
  930 #ifdef SEM_DEBUG
  931                 printf("didn't find it and wasn't asked to create it\n");
  932 #endif
  933                 UNLOCK_AND_RETURN(ENOENT);
  934         }
  935 
  936 found:
  937         *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
  938         AUDIT_ARG(svipc_id, *retval);
  939 #ifdef SEM_DEBUG
  940         printf("semget is done, returning %d\n", *retval);
  941 #endif
  942         SUBSYSTEM_LOCK_RELEASE;
  943         return(0);
  944 }
  945 
  946 #ifndef _SYS_SYSPROTO_H_
  947 struct semop_args {
  948         int     semid;
  949         struct  sembuf *sops;
  950         int     nsops;
  951 };
  952 #endif
  953 
  954 int
  955 semop(p, uap, retval)
  956         struct proc *p;
  957         register struct semop_args *uap;
  958         register_t *retval;
  959 {
  960         int semid = uap->semid;
  961         int nsops = uap->nsops;
  962         struct sembuf sops[MAX_SOPS];
  963         register struct semid_ds *semaptr;
  964         register struct sembuf *sopptr;
  965         register struct sem *semptr;
  966         struct sem_undo *suptr = NULL;
  967         struct ucred *cred = p->p_ucred;
  968         int i, j, eval;
  969         int do_wakeup, do_undos;
  970 
  971         AUDIT_ARG(svipc_id, uap->semid);
  972         SUBSYSTEM_LOCK_AQUIRE(p);
  973 #ifdef SEM_DEBUG
  974         printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
  975 #endif
  976 
  977         semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
  978 
  979         if (semid < 0 || semid >= seminfo.semmni)
  980                 UNLOCK_AND_RETURN(EINVAL);
  981 
  982         semaptr = &sema[semid];
  983         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
  984                 UNLOCK_AND_RETURN(EINVAL);
  985         if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
  986                 UNLOCK_AND_RETURN(EINVAL);
  987 
  988         if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
  989 #ifdef SEM_DEBUG
  990                 printf("eval = %d from ipaccess\n", eval);
  991 #endif
  992                 UNLOCK_AND_RETURN(eval);
  993         }
  994 
  995         if (nsops > MAX_SOPS) {
  996 #ifdef SEM_DEBUG
  997                 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
  998 #endif
  999                 UNLOCK_AND_RETURN(E2BIG);
 1000         }
 1001 
 1002         if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
 1003 #ifdef SEM_DEBUG
 1004                 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
 1005                     uap->sops, &sops, nsops * sizeof(sops[0]));
 1006 #endif
 1007                 UNLOCK_AND_RETURN(eval);
 1008         }
 1009 
 1010         /*
 1011          * Loop trying to satisfy the vector of requests.
 1012          * If we reach a point where we must wait, any requests already
 1013          * performed are rolled back and we go to sleep until some other
 1014          * process wakes us up.  At this point, we start all over again.
 1015          *
 1016          * This ensures that from the perspective of other tasks, a set
 1017          * of requests is atomic (never partially satisfied).
 1018          */
 1019         do_undos = 0;
 1020 
 1021         for (;;) {
 1022                 do_wakeup = 0;
 1023 
 1024                 for (i = 0; i < nsops; i++) {
 1025                         sopptr = &sops[i];
 1026 
 1027                         if (sopptr->sem_num >= semaptr->sem_nsems)
 1028                                 UNLOCK_AND_RETURN(EFBIG);
 1029 
 1030                         semptr = &semaptr->sem_base[sopptr->sem_num];
 1031 
 1032 #ifdef SEM_DEBUG
 1033                         printf("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
 1034                             semaptr, semaptr->sem_base, semptr,
 1035                             sopptr->sem_num, semptr->semval, sopptr->sem_op,
 1036                             (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
 1037 #endif
 1038 
 1039                         if (sopptr->sem_op < 0) {
 1040                                 if (semptr->semval + sopptr->sem_op < 0) {
 1041 #ifdef SEM_DEBUG
 1042                                         printf("semop:  can't do it now\n");
 1043 #endif
 1044                                         break;
 1045                                 } else {
 1046                                         semptr->semval += sopptr->sem_op;
 1047                                         if (semptr->semval == 0 &&
 1048                                             semptr->semzcnt > 0)
 1049                                                 do_wakeup = 1;
 1050                                 }
 1051                                 if (sopptr->sem_flg & SEM_UNDO)
 1052                                         do_undos = 1;
 1053                         } else if (sopptr->sem_op == 0) {
 1054                                 if (semptr->semval > 0) {
 1055 #ifdef SEM_DEBUG
 1056                                         printf("semop:  not zero now\n");
 1057 #endif
 1058                                         break;
 1059                                 }
 1060                         } else {
 1061                                 if (semptr->semncnt > 0)
 1062                                         do_wakeup = 1;
 1063                                 semptr->semval += sopptr->sem_op;
 1064                                 if (sopptr->sem_flg & SEM_UNDO)
 1065                                         do_undos = 1;
 1066                         }
 1067                 }
 1068 
 1069                 /*
 1070                  * Did we get through the entire vector?
 1071                  */
 1072                 if (i >= nsops)
 1073                         goto done;
 1074 
 1075                 /*
 1076                  * No ... rollback anything that we've already done
 1077                  */
 1078 #ifdef SEM_DEBUG
 1079                 printf("semop:  rollback 0 through %d\n", i-1);
 1080 #endif
 1081                 for (j = 0; j < i; j++)
 1082                         semaptr->sem_base[sops[j].sem_num].semval -=
 1083                             sops[j].sem_op;
 1084 
 1085                 /*
 1086                  * If the request that we couldn't satisfy has the
 1087                  * NOWAIT flag set then return with EAGAIN.
 1088                  */
 1089                 if (sopptr->sem_flg & IPC_NOWAIT)
 1090                         UNLOCK_AND_RETURN(EAGAIN);
 1091 
 1092                 if (sopptr->sem_op == 0)
 1093                         semptr->semzcnt++;
 1094                 else
 1095                         semptr->semncnt++;
 1096 
 1097 #ifdef SEM_DEBUG
 1098                 printf("semop:  good night!\n");
 1099 #endif
 1100                 /* Release our lock on the semaphore subsystem so
 1101                  * another thread can get at the semaphore we are
 1102                  * waiting for. We will get the lock back after we
 1103                  * wake up.
 1104                  */
 1105                 SUBSYSTEM_LOCK_RELEASE;
 1106                 sysv_sem_sleeping_threads++;
 1107                 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
 1108                     "semwait", 0);
 1109                 sysv_sem_sleeping_threads--;
 1110                 
 1111 #ifdef SEM_DEBUG
 1112                 printf("semop:  good morning (eval=%d)!\n", eval);
 1113 #endif
 1114                 /* There is no need to get the lock if we are just
 1115                  * going to return without performing more semaphore
 1116                  * operations.
 1117                  */
 1118                 if (eval != 0)
 1119                         return(EINTR);
 1120 
 1121                 SUBSYSTEM_LOCK_AQUIRE(p);       /* Get it back */
 1122                 suptr = NULL;   /* sem_undo may have been reallocated */
 1123                 semaptr = &sema[semid];    /* sema may have been reallocated */
 1124 
 1125 
 1126 #ifdef SEM_DEBUG
 1127                 printf("semop:  good morning!\n");
 1128 #endif
 1129 
 1130                 /*
 1131                  * Make sure that the semaphore still exists
 1132                  */
 1133                 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
 1134                     semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
 1135                         /* The man page says to return EIDRM. */
 1136                         /* Unfortunately, BSD doesn't define that code! */
 1137 #ifdef EIDRM
 1138                         UNLOCK_AND_RETURN(EIDRM);
 1139 #else
 1140                         UNLOCK_AND_RETURN(EINVAL);
 1141 #endif
 1142                 }
 1143 
 1144                 /*
 1145                  * The semaphore is still alive.  Readjust the count of
 1146                  * waiting processes. semptr needs to be recomputed
 1147                  * because the sem[] may have been reallocated while
 1148                  * we were sleeping, updating our sem_base pointer.
 1149                  */
 1150                 semptr = &semaptr->sem_base[sopptr->sem_num];
 1151                 if (sopptr->sem_op == 0)
 1152                         semptr->semzcnt--;
 1153                 else
 1154                         semptr->semncnt--;
 1155         }
 1156 
 1157 done:
 1158         /*
 1159          * Process any SEM_UNDO requests.
 1160          */
 1161         if (do_undos) {
 1162                 for (i = 0; i < nsops; i++) {
 1163                         /*
 1164                          * We only need to deal with SEM_UNDO's for non-zero
 1165                          * op's.
 1166                          */
 1167                         int adjval;
 1168 
 1169                         if ((sops[i].sem_flg & SEM_UNDO) == 0)
 1170                                 continue;
 1171                         adjval = sops[i].sem_op;
 1172                         if (adjval == 0)
 1173                                 continue;
 1174                         eval = semundo_adjust(p, &suptr, semid,
 1175                             sops[i].sem_num, -adjval);
 1176                         if (eval == 0)
 1177                                 continue;
 1178 
 1179                         /*
 1180                          * Oh-Oh!  We ran out of either sem_undo's or undo's.
 1181                          * Rollback the adjustments to this point and then
 1182                          * rollback the semaphore ups and down so we can return
 1183                          * with an error with all structures restored.  We
 1184                          * rollback the undo's in the exact reverse order that
 1185                          * we applied them.  This guarantees that we won't run
 1186                          * out of space as we roll things back out.
 1187                          */
 1188                         for (j = i - 1; j >= 0; j--) {
 1189                                 if ((sops[j].sem_flg & SEM_UNDO) == 0)
 1190                                         continue;
 1191                                 adjval = sops[j].sem_op;
 1192                                 if (adjval == 0)
 1193                                         continue;
 1194                                 if (semundo_adjust(p, &suptr, semid,
 1195                                     sops[j].sem_num, adjval) != 0)
 1196                                         panic("semop - can't undo undos");
 1197                         }
 1198 
 1199                         for (j = 0; j < nsops; j++)
 1200                                 semaptr->sem_base[sops[j].sem_num].semval -=
 1201                                     sops[j].sem_op;
 1202 
 1203 #ifdef SEM_DEBUG
 1204                         printf("eval = %d from semundo_adjust\n", eval);
 1205 #endif
 1206                         UNLOCK_AND_RETURN(eval);
 1207                 } /* loop through the sops */
 1208         } /* if (do_undos) */
 1209 
 1210         /* We're definitely done - set the sempid's */
 1211         for (i = 0; i < nsops; i++) {
 1212                 sopptr = &sops[i];
 1213                 semptr = &semaptr->sem_base[sopptr->sem_num];
 1214                 semptr->sempid = p->p_pid;
 1215         }
 1216 
 1217         /* Do a wakeup if any semaphore was up'd.
 1218          *  we will release our lock on the semaphore subsystem before
 1219          *  we wakeup other processes to prevent a little thrashing.
 1220          *  Note that this is fine because we are done using the
 1221          *  semaphore structures at this point in time.  We only use
 1222          *  a local variable pointer value, and the retval
 1223          *  parameter.
 1224          *  Note 2: Future use of sem_wakeup may reqiure the lock.
 1225          */
 1226         SUBSYSTEM_LOCK_RELEASE;
 1227         if (do_wakeup) {
 1228 #ifdef SEM_DEBUG
 1229                 printf("semop:  doing wakeup\n");
 1230 #ifdef SEM_WAKEUP
 1231                 sem_wakeup((caddr_t)semaptr);
 1232 #else
 1233                 wakeup((caddr_t)semaptr);
 1234 #endif
 1235                 printf("semop:  back from wakeup\n");
 1236 #else
 1237                 wakeup((caddr_t)semaptr);
 1238 #endif
 1239         }
 1240 #ifdef SEM_DEBUG
 1241         printf("semop:  done\n");
 1242 #endif
 1243         *retval = 0;
 1244         return(0);
 1245 }
 1246 
 1247 /*
 1248  * Go through the undo structures for this process and apply the adjustments to
 1249  * semaphores.
 1250  */
 1251 void
 1252 semexit(p)
 1253         struct proc *p;
 1254 {
 1255         register struct sem_undo *suptr;
 1256         register struct sem_undo **supptr;
 1257         int did_something;
 1258 
 1259         /* If we have not allocated our semaphores yet there can't be
 1260          * anything to undo, but we need the lock to prevent
 1261          * dynamic memory race conditions.
 1262          */
 1263         SUBSYSTEM_LOCK_AQUIRE(p);
 1264         if (!sem)
 1265         {
 1266                 SUBSYSTEM_LOCK_RELEASE;
 1267                 return;
 1268         }
 1269         did_something = 0;
 1270 
 1271         /*
 1272          * Go through the chain of undo vectors looking for one
 1273          * associated with this process.
 1274          */
 1275 
 1276         for (supptr = &semu_list; (suptr = *supptr) != NULL;
 1277             supptr = &suptr->un_next) {
 1278                 if (suptr->un_proc == p)
 1279                         break;
 1280         }
 1281 
 1282         if (suptr == NULL)
 1283                 goto unlock;
 1284 
 1285 #ifdef SEM_DEBUG
 1286         printf("proc @%08x has undo structure with %d entries\n", p,
 1287             suptr->un_cnt);
 1288 #endif
 1289 
 1290         /*
 1291          * If there are any active undo elements then process them.
 1292          */
 1293         if (suptr->un_cnt > 0) {
 1294                 int ix;
 1295 
 1296                 for (ix = 0; ix < suptr->un_cnt; ix++) {
 1297                         int semid = suptr->un_ent[ix].un_id;
 1298                         int semnum = suptr->un_ent[ix].un_num;
 1299                         int adjval = suptr->un_ent[ix].un_adjval;
 1300                         struct semid_ds *semaptr;
 1301 
 1302                         semaptr = &sema[semid];
 1303                         if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
 1304                                 panic("semexit - semid not allocated");
 1305                         if (semnum >= semaptr->sem_nsems)
 1306                                 panic("semexit - semnum out of range");
 1307 
 1308 #ifdef SEM_DEBUG
 1309                         printf("semexit:  %08x id=%d num=%d(adj=%d) ; sem=%d\n",
 1310                             suptr->un_proc, suptr->un_ent[ix].un_id,
 1311                             suptr->un_ent[ix].un_num,
 1312                             suptr->un_ent[ix].un_adjval,
 1313                             semaptr->sem_base[semnum].semval);
 1314 #endif
 1315 
 1316                         if (adjval < 0) {
 1317                                 if (semaptr->sem_base[semnum].semval < -adjval)
 1318                                         semaptr->sem_base[semnum].semval = 0;
 1319                                 else
 1320                                         semaptr->sem_base[semnum].semval +=
 1321                                             adjval;
 1322                         } else
 1323                                 semaptr->sem_base[semnum].semval += adjval;
 1324 
 1325                 /* Maybe we should build a list of semaptr's to wake
 1326                  * up, finish all access to data structures, release the
 1327                  * subsystem lock, and wake all the processes.  Something
 1328                  * to think about.  It wouldn't buy us anything unless
 1329                  * wakeup had the potential to block, or the syscall
 1330                  * funnel state was changed to allow multiple threads
 1331                  * in the BSD code at once.
 1332                  */
 1333 #ifdef SEM_WAKEUP
 1334                         sem_wakeup((caddr_t)semaptr);
 1335 #else
 1336                         wakeup((caddr_t)semaptr);
 1337 #endif
 1338 #ifdef SEM_DEBUG
 1339                         printf("semexit:  back from wakeup\n");
 1340 #endif
 1341                 }
 1342         }
 1343 
 1344         /*
 1345          * Deallocate the undo vector.
 1346          */
 1347 #ifdef SEM_DEBUG
 1348         printf("removing vector\n");
 1349 #endif
 1350         suptr->un_proc = NULL;
 1351         *supptr = suptr->un_next;
 1352 
 1353 unlock:
 1354         /*
 1355          * There is a semaphore leak (i.e. memory leak) in this code.
 1356          * We should be deleting the IPC_PRIVATE semaphores when they are
 1357          * no longer needed, and we dont. We would have to track which processes
 1358          * know about which IPC_PRIVATE semaphores, updating the list after
 1359          * every fork.  We can't just delete them semaphore when the process
 1360          * that created it dies, because that process may well have forked
 1361          * some children.  So we need to wait until all of it's children have
 1362          * died, and so on.  Maybe we should tag each IPC_PRIVATE sempahore
 1363          * with the creating group ID, count the number of processes left in
 1364          * that group, and delete the semaphore when the group is gone.
 1365          * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
 1366          * There is an upper bound on the size of our semaphore array, so   
 1367          * leaking the semaphores should not work as a DOS attack.
 1368          *
 1369          * Please note that the original BSD code this file is based on had the
 1370          * same leaky semaphore problem.
 1371          */
 1372 
 1373         SUBSYSTEM_LOCK_RELEASE;
 1374 }
 1375 /* (struct sysctl_oid *oidp, void *arg1, int arg2, \
 1376         struct sysctl_req *req) */
 1377 static int
 1378 sysctl_seminfo SYSCTL_HANDLER_ARGS
 1379 {
 1380         int error = 0;
 1381 
 1382         error = SYSCTL_OUT(req, arg1, sizeof(int));
 1383         if (error || !req->newptr)
 1384                 return(error);
 1385 
 1386         SUBSYSTEM_LOCK_AQUIRE(current_proc());
 1387         /* Set the values only if shared memory is not initialised */
 1388         if ((sem == (struct sem *) 0) && 
 1389                 (sema == (struct semid_ds *) 0) && 
 1390                 (semu == (struct semid_ds *) 0) && 
 1391                 (semu_list == (struct sem_undo *) 0)) {
 1392                         if (error = SYSCTL_IN(req, arg1, sizeof(int))) {
 1393                                 goto out;
 1394                         }
 1395         } else 
 1396                 error = EINVAL;
 1397 out:
 1398         SUBSYSTEM_LOCK_RELEASE;
 1399         return(error);
 1400         
 1401 }
 1402 
 1403 /* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
 1404 extern struct sysctl_oid_list sysctl__kern_sysv_children;
 1405 SYSCTL_PROC(_kern_sysv, KSYSV_SEMMNI, semmni, CTLTYPE_INT | CTLFLAG_RW,
 1406     &limitseminfo.semmni, 0, &sysctl_seminfo ,"I","semmni");
 1407 
 1408 SYSCTL_PROC(_kern_sysv, KSYSV_SEMMNS, semmns, CTLTYPE_INT | CTLFLAG_RW,
 1409     &limitseminfo.semmns, 0, &sysctl_seminfo ,"I","semmns");
 1410 
 1411 SYSCTL_PROC(_kern_sysv, KSYSV_SEMMNU, semmnu, CTLTYPE_INT | CTLFLAG_RW,
 1412     &limitseminfo.semmnu, 0, &sysctl_seminfo ,"I","semmnu");
 1413 
 1414 SYSCTL_PROC(_kern_sysv, KSYSV_SEMMSL, semmsl, CTLTYPE_INT | CTLFLAG_RW,
 1415     &limitseminfo.semmsl, 0, &sysctl_seminfo ,"I","semmsl");
 1416     
 1417 SYSCTL_PROC(_kern_sysv, KSYSV_SEMUNE, semume, CTLTYPE_INT | CTLFLAG_RW,
 1418     &limitseminfo.semume, 0, &sysctl_seminfo ,"I","semume");
 1419 
 1420 

Cache object: f7f422befc59bc783c1491a290b5c774


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