]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/sysv_sem.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / bsd / kern / sysv_sem.c
index 8f7b26537c25e1b34e3638dd26fd71ba171af894..24bc5cfada9665983a4ee330fb5d6e456f9c1dc3 100644 (file)
@@ -248,12 +248,6 @@ grow_semu_array(int newSize)
 {
        register int i;
        register struct sem_undo *newSemu;
-       static boolean_t grow_semu_array_in_progress = FALSE;
-
-       while (grow_semu_array_in_progress) {
-               msleep(&grow_semu_array_in_progress, &sysv_sem_subsys_mutex,
-                      PPAUSE, "grow_semu_array", NULL);
-       }
 
        if (newSize <= seminfo.semmnu)
                return 1;
@@ -271,13 +265,8 @@ grow_semu_array(int newSize)
 #ifdef SEM_DEBUG
        printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize);
 #endif
-       grow_semu_array_in_progress = TRUE;
-       SYSV_SEM_SUBSYS_UNLOCK();
-       MALLOC(newSemu, struct sem_undo*, sizeof(struct sem_undo) * newSize,
-               M_SYSVSEM, M_WAITOK);
-       SYSV_SEM_SUBSYS_LOCK();
-       grow_semu_array_in_progress = FALSE;
-       wakeup((caddr_t) &grow_semu_array_in_progress);
+       MALLOC(newSemu, struct sem_undo *, sizeof (struct sem_undo) * newSize,
+              M_SYSVSEM, M_WAITOK | M_ZERO);
        if (NULL == newSemu)
        {
 #ifdef SEM_DEBUG
@@ -286,15 +275,16 @@ grow_semu_array(int newSize)
                return 0;
        }
 
-               /* Initialize our structure.  */
+               /* copy the old data to the new array */
        for (i = 0; i < seminfo.semmnu; i++)
        {
                newSemu[i] = semu[i];
        }
-               for (i = seminfo.semmnu; i < newSize; i++)
-        {
-                       newSemu[i].un_proc = NULL;
-        }
+       /*
+        * The new elements (from newSemu[i] to newSemu[newSize-1]) have their
+        * "un_proc" set to 0 (i.e. NULL) by the M_ZERO flag to MALLOC() above,
+        * so they're already marked as "not in use".
+        */
 
        /* Clean up the old array */
        if (semu)
@@ -336,8 +326,9 @@ grow_sema_array(int newSize)
 #ifdef SEM_DEBUG
        printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize);
 #endif
-       MALLOC(newSema, struct user_semid_ds *, sizeof(struct user_semid_ds) * newSize,
-               M_SYSVSEM, M_WAITOK);
+       MALLOC(newSema, struct user_semid_ds *,
+              sizeof (struct user_semid_ds) * newSize,
+              M_SYSVSEM, M_WAITOK | M_ZERO);
        if (NULL == newSema)
        {
 #ifdef SEM_DEBUG
@@ -346,7 +337,7 @@ grow_sema_array(int newSize)
                return 0;
        }
 
-       /* Initialize our new ids, and copy over the old ones */
+       /* copy over the old ids */
        for (i = 0; i < seminfo.semmni; i++)
        {
                newSema[i] = sema[i];
@@ -361,12 +352,11 @@ grow_sema_array(int newSize)
                if (sema[i].sem_perm.mode & SEM_ALLOC)
                        wakeup((caddr_t)&sema[i]);
        }
-
-       for (i = seminfo.semmni; i < newSize; i++)
-       {
-               newSema[i].sem_base = NULL;
-               newSema[i].sem_perm.mode = 0;
-       }
+       /*
+        * The new elements (from newSema[i] to newSema[newSize-1]) have their
+        * "sem_base" and "sem_perm.mode" set to 0 (i.e. NULL) by the M_ZERO
+        * flag to MALLOC() above, so they're already marked as "not in use".
+        */
 
        /* Clean up the old array */
        if (sema)
@@ -410,8 +400,8 @@ grow_sem_pool(int new_pool_size)
 #ifdef SEM_DEBUG
        printf("growing sem_pool array from %d to %d\n", seminfo.semmns, new_pool_size);
 #endif
-       MALLOC(new_sem_pool, struct sem *, sizeof(struct sem) * new_pool_size,
-               M_SYSVSEM, M_WAITOK);
+       MALLOC(new_sem_pool, struct sem *, sizeof (struct sem) * new_pool_size,
+              M_SYSVSEM, M_WAITOK | M_ZERO);
        if (NULL == new_sem_pool) {
 #ifdef SEM_DEBUG
                printf("allocation failed.  no changes made.\n");
@@ -535,8 +525,9 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid,
        register struct undo *sueptr, **suepptr, *new_sueptr;
        int i;
 
-       /* Look for and remember the sem_undo if the caller doesn't provide
-          it */
+       /*
+        * Look for and remember the sem_undo if the caller doesn't provide it
+        */
 
        suptr = *supptr;
        if (suptr == NULL) {
@@ -562,7 +553,6 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid,
         * 0).
         */
        new_sueptr = NULL;
-lookup:
        for (i = 0, suepptr = &suptr->un_ent, sueptr = suptr->un_ent;
             i < suptr->un_cnt;
             i++, suepptr = &sueptr->une_next, sueptr = sueptr->une_next) {
@@ -578,61 +568,36 @@ lookup:
                        FREE(sueptr, M_SYSVSEM);
                        sueptr = NULL;
                }
-               if (new_sueptr != NULL) {
-                       /*
-                        * We lost the race: free the "undo" entry we allocated
-                        * and use the one that won.
-                        */
-                       FREE(new_sueptr, M_SYSVSEM);
-                       new_sueptr = NULL;
-               }
-               return(0);
+               return 0;
        }
 
        /* Didn't find the right entry - create it */
        if (adjval == 0) {
-               if (new_sueptr != NULL) {
-                       FREE(new_sueptr, M_SYSVSEM);
-                       new_sueptr = NULL;
-               }
-               return(0);
+               /* no adjustment: no need for a new entry */
+               return 0;
        }
 
-       if (new_sueptr != NULL) {
-               /*
-                * Use the new "undo" entry we allocated in the previous pass
-                */
-               new_sueptr->une_next = suptr->un_ent;
-               suptr->un_ent = new_sueptr;
-               suptr->un_cnt++;
-               new_sueptr->une_adjval = adjval;
-               new_sueptr->une_id = semid;
-               new_sueptr->une_num = semnum;
-               return 0;
+       if (suptr->un_cnt == limitseminfo.semume) {
+               /* reached the limit number of semaphore undo entries */
+               return EINVAL;
        }
 
-       if (suptr->un_cnt != limitseminfo.semume) {
-               SYSV_SEM_SUBSYS_UNLOCK();
-               /*
-                * Unlocking opens the door to race conditions.  Someone else
-                * could be trying to allocate the same thing at this point,
-                * so we'll have to check if we lost the race.
-                */
-               MALLOC(new_sueptr, struct undo *, sizeof (struct undo),
-                      M_SYSVSEM, M_WAITOK);
-               SYSV_SEM_SUBSYS_LOCK();
-               if (new_sueptr == NULL) {
-                       return ENOMEM;
-               }
-               /*
-                * There might be other threads doing the same thing for this
-                * process, so check again if an "undo" entry exists for that
-                * semaphore.
-                */
-               goto lookup;
-       } else
-               return(EINVAL);
-       return(0);
+       /* allocate a new semaphore undo entry */
+       MALLOC(new_sueptr, struct undo *, sizeof (struct undo),
+              M_SYSVSEM, M_WAITOK);
+       if (new_sueptr == NULL) {
+               return ENOMEM;
+       }
+
+       /* fill in the new semaphore undo entry */
+       new_sueptr->une_next = suptr->un_ent;
+       suptr->un_ent = new_sueptr;
+       suptr->un_cnt++;
+       new_sueptr->une_adjval = adjval;
+       new_sueptr->une_id = semid;
+       new_sueptr->une_num = semnum;
+
+       return 0;
 }
 
 /* Assumes we already hold the subsystem lock.
@@ -742,8 +707,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
                                goto semctlout;
 
-               SYSV_SEM_SUBSYS_UNLOCK();
-
                if (IS_64BIT_PROCESS(p)) {
                        eval = copyin(user_arg.buf, &sbuf, sizeof(struct user_semid_ds));
                } else {
@@ -752,10 +715,9 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                        semid_ds_32to64((struct semid_ds *)&sbuf, &sbuf);
                }
                
-               if (eval != 0)
-                       return(eval);
-
-               SYSV_SEM_SUBSYS_LOCK();
+               if (eval != 0) {
+                       goto semctlout;
+               }
 
                semaptr->sem_perm.uid = sbuf.sem_perm.uid;
                semaptr->sem_perm.gid = sbuf.sem_perm.gid;
@@ -768,7 +730,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
                                goto semctlout;
                bcopy(semaptr, &uds, sizeof(struct user_semid_ds));
-               SYSV_SEM_SUBSYS_UNLOCK();
                if (IS_64BIT_PROCESS(p)) {
                        eval = copyout(&uds, user_arg.buf, sizeof(struct user_semid_ds));
                } else {
@@ -776,7 +737,6 @@ semctl(struct proc *p, struct semctl_args *uap, register_t *retval)
                        semid_ds_64to32(&uds, &semid_ds32);
                        eval = copyout(&semid_ds32, user_arg.buf, sizeof(struct semid_ds));
                }
-               SYSV_SEM_SUBSYS_LOCK();
                break;
 
        case GETNCNT:
@@ -904,7 +864,7 @@ semget(__unused struct proc *p, struct semget_args *uap, register_t *retval)
 #endif
 
 
-               SYSV_SEM_SUBSYS_LOCK();
+       SYSV_SEM_SUBSYS_LOCK();
 
     
        if (key != IPC_PRIVATE) {
@@ -1190,42 +1150,44 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval)
 #ifdef SEM_DEBUG
                printf("semop:  good morning (eval=%d)!\n", eval);
 #endif
-               /* we need the lock here due to mods on semptr */
                if (eval != 0) {
-                       if (sopptr->sem_op == 0)
-                               semptr->semzcnt--;
-                       else
-                               semptr->semncnt--;
-
                        eval = EINTR;
-                       goto semopout;
                }
 
+               /*
+                * IMPORTANT: while we were asleep, the semaphore array might
+                * have been reallocated somewhere else (see grow_sema_array()).
+                * When we wake up, we have to re-lookup the semaphore 
+                * structures and re-validate them.
+                */
+
                suptr = NULL;   /* sem_undo may have been reallocated */
                semaptr = &sema[semid];    /* sema may have been reallocated */
 
-
-#ifdef SEM_DEBUG
-               printf("semop:  good morning!\n");
-#endif
-
                /*
                 * Make sure that the semaphore still exists
                 */
                if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
-                   semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
-                       /* The man page says to return EIDRM. */
-                       /* Unfortunately, BSD doesn't define that code! */
-                       if (sopptr->sem_op == 0)
-                               semptr->semzcnt--;
-                       else
-                               semptr->semncnt--;
+                   semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid) ||
+                   sopptr->sem_num >= semaptr->sem_nsems) {
+                       if (eval == EINTR) {
+                               /*
+                                * EINTR takes precedence over the fact that
+                                * the semaphore disappeared while we were
+                                * sleeping...
+                                */
+                       } else {
+                               /*
+                                * The man page says to return EIDRM.
+                                * Unfortunately, BSD doesn't define that code!
+                                */
 #ifdef EIDRM
-               eval = EIDRM;
+                               eval = EIDRM;
 #else
-               eval = EINVAL;
+                               eval = EINVAL;
 #endif
-               goto semopout;
+                       }
+                       goto semopout;
                }
 
                /*
@@ -1239,6 +1201,10 @@ semop(struct proc *p, struct semop_args *uap, register_t *retval)
                        semptr->semzcnt--;
                else
                        semptr->semncnt--;
+
+               if (eval != 0) { /* EINTR */
+                       goto semopout;
+               }
        }
 
 done:
@@ -1553,9 +1519,7 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
                        error = EINVAL;
                        break;
                }
-               SYSV_SEM_SUBSYS_UNLOCK();
                error = copyout(&seminfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
-               SYSV_SEM_SUBSYS_LOCK();
                break;
 
        case IPCS_SEM_ITER:     /* Iterate over existing segments */
@@ -1588,14 +1552,12 @@ IPCS_sem_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
                        semid_ds_64to32(semid_dsp, &semid_ds32);
                        semid_dsp = &semid_ds32;
                }
-               SYSV_SEM_SUBSYS_UNLOCK();
                error = copyout(semid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
                if (!error) {
                        /* update cursor */
                        ipcs.u64.ipcs_cursor = cursor + 1;
                        error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
                }
-               SYSV_SEM_SUBSYS_LOCK();
                break;
 
        default: