The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sysv_sem.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: e74c7244884230b72be4b4ffccec192d


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