kernel - Fix MP race in sysv semaphore code
[dragonfly.git] / sys / kern / sysv_sem.c
index caec18d..6ba0ced 100644 (file)
@@ -1,5 +1,4 @@
 /* $FreeBSD: src/sys/kern/sysv_sem.c,v 1.69 2004/03/17 09:37:13 cperciva Exp $ */
-/* $DragonFly: src/sys/kern/sysv_sem.c,v 1.19 2008/01/06 16:55:51 swildner Exp $ */
 
 /*
  * Implementation of SVID semaphores
@@ -21,6 +20,9 @@
 #include <sys/sysctl.h>
 #include <sys/malloc.h>
 #include <sys/jail.h>
+#include <sys/thread.h>
+
+#include <sys/thread2.h>
 
 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores");
 
@@ -37,6 +39,7 @@ static sy_call_t *semcalls[] = {
        (sy_call_t *)sys_semop
 };
 
+static struct lwkt_token semu_token = LWKT_TOKEN_INITIALIZER(semu_token);
 static int     semtot = 0;
 static struct semid_ds *sema;  /* semaphore id pool */
 static struct sem *sem;                /* semaphore pool */
@@ -68,10 +71,10 @@ struct sem_undo {
  * Configuration parameters
  */
 #ifndef SEMMNI
-#define SEMMNI 10              /* # of semaphore identifiers */
+#define SEMMNI 22              /* # of semaphore identifiers */
 #endif
 #ifndef SEMMNS
-#define SEMMNS 60              /* # of semaphores in system */
+#define SEMMNS 341             /* # of semaphores in system */
 #endif
 #ifndef SEMUME
 #define SEMUME 10              /* max # of undo entries per process */
@@ -107,7 +110,8 @@ struct sem_undo {
 /*
  * Macro to find a particular sem_undo vector
  */
-#define SEMU(ix)       ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
+#define SEMU(ix)       ((struct sem_undo *)(((intptr_t)semu) + (ix) * \
+                                            seminfo.semusz))
 
 /*
  * semaphore info struct
@@ -136,16 +140,26 @@ TUNABLE_INT("kern.ipc.semusz", &seminfo.semusz);
 TUNABLE_INT("kern.ipc.semvmx", &seminfo.semvmx);
 TUNABLE_INT("kern.ipc.semaem", &seminfo.semaem);
 
-SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, "");
-SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, "");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0,
+    "Number of entries in semaphore map");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0,
+    "Number of semaphore identifiers");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0,
+    "Total number of semaphores");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0,
+    "Total number of undo structures");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0,
+    "Max number of semaphores per id");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0,
+    "Max number of operations per semop call");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0,
+    "Max number of undo entries per process");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0,
+    "Size in bytes of undo structure");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0,
+    "Semaphore maximum value");
+SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0,
+    "Adjust on exit max value");
 
 #if 0
 RO seminfo.semmap      /* SEMMAP unused */
@@ -185,28 +199,33 @@ SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL)
  * Entry point for all SEM calls
  *
  * semsys_args(int which, a2, a3, ...) (VARARGS)
+ *
+ * MPALMOSTSAFE
  */
 int
 sys_semsys(struct semsys_args *uap)
 {
-       struct proc *p = curproc;
+       struct thread *td = curthread;
        unsigned int which = (unsigned int)uap->which;
+       int error;
 
-       if (!jail_sysvipc_allowed && p->p_ucred->cr_prison != NULL)
+       if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
                return (ENOSYS);
 
-       if (which >= sizeof(semcalls)/sizeof(semcalls[0]))
+       if (which >= NELEM(semcalls))
                return (EINVAL);
        bcopy(&uap->a2, &uap->which,
-           sizeof(struct semsys_args) - offsetof(struct semsys_args, a2));
-       return ((*semcalls[which])(uap));
+             sizeof(struct semsys_args) - offsetof(struct semsys_args, a2));
+       error = (*semcalls[which])(uap);
+       return (error);
 }
 
 /*
  * Allocate a new sem_undo structure for a process
  * (returns ptr to structure or NULL if no more room)
+ *
+ * semu_token is held by the caller.
  */
-
 static struct sem_undo *
 semu_alloc(struct proc *p)
 {
@@ -220,13 +239,11 @@ semu_alloc(struct proc *p)
         * (we'll purge any empty structures after the first pass so
         * two passes are always enough)
         */
-
        for (attempt = 0; attempt < 2; attempt++) {
                /*
                 * Look for a free structure.
                 * Fill it in and return it if we find one.
                 */
-
                for (i = 0; i < seminfo.semmnu; i++) {
                        suptr = SEMU(i);
                        if (suptr->un_proc == NULL) {
@@ -234,7 +251,7 @@ semu_alloc(struct proc *p)
                                semu_list = suptr;
                                suptr->un_cnt = 0;
                                suptr->un_proc = p;
-                               return(suptr);
+                               goto done;
                        }
                }
 
@@ -253,13 +270,16 @@ semu_alloc(struct proc *p)
                                        suptr->un_proc = NULL;
                                        *supptr = suptr->un_next;
                                        did_something = 1;
-                               } else
+                               } else {
                                        supptr = &(suptr->un_next);
+                               }
                        }
 
                        /* If we didn't free anything then just give-up */
-                       if (!did_something)
-                               return(NULL);
+                       if (!did_something) {
+                               suptr = NULL;
+                               goto done;
+                       }
                } else {
                        /*
                         * The second pass failed even though we freed
@@ -269,7 +289,9 @@ semu_alloc(struct proc *p)
                        panic("semu_alloc - second attempt failed");
                }
        }
-       return (NULL);
+       suptr = NULL;
+done:
+       return (suptr);
 }
 
 /*
@@ -283,10 +305,14 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
        struct sem_undo *suptr;
        struct undo *sunptr;
        int i;
+       int error = 0;
 
-       /* 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.
+        */
+       lwkt_gettoken(&semu_token);
+       lwkt_gettoken(&p->p_token);
        suptr = *supptr;
        if (suptr == NULL) {
                for (suptr = semu_list; suptr != NULL;
@@ -298,10 +324,13 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
                }
                if (suptr == NULL) {
                        if (adjval == 0)
-                               return(0);
+                               goto done;
+                       p->p_flags |= P_SYSVSEM;
                        suptr = semu_alloc(p);
-                       if (suptr == NULL)
-                               return(ENOSPC);
+                       if (suptr == NULL) {
+                               error = ENOSPC;
+                               goto done;
+                       }
                        *supptr = suptr;
                }
        }
@@ -324,20 +353,24 @@ semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
                                suptr->un_ent[i] =
                                    suptr->un_ent[suptr->un_cnt];
                }
-               return(0);
+               goto done;
        }
 
        /* Didn't find the right entry - create it */
        if (adjval == 0)
-               return(0);
+               goto done;
        if (suptr->un_cnt != seminfo.semume) {
                sunptr = &suptr->un_ent[suptr->un_cnt];
                suptr->un_cnt++;
                sunptr->un_adjval = adjval;
                sunptr->un_id = semid; sunptr->un_num = semnum;
-       } else
-               return(EINVAL);
-       return(0);
+       } else {
+               error = EINVAL;
+       }
+done:
+       lwkt_reltoken(&p->p_token);
+       lwkt_reltoken(&semu_token);
+       return (error);
 }
 
 static void
@@ -345,6 +378,7 @@ semundo_clear(int semid, int semnum)
 {
        struct sem_undo *suptr;
 
+       lwkt_gettoken(&semu_token);
        for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
                struct undo *sunptr = &suptr->un_ent[0];
                int i = 0;
@@ -365,49 +399,83 @@ semundo_clear(int semid, int semnum)
                        i++, sunptr++;
                }
        }
+       lwkt_reltoken(&semu_token);
 }
 
 /*
  * Note that the user-mode half of this passes a union, not a pointer
+ *
+ * MPALMOSTSAFE
  */
-
 int
 sys___semctl(struct __semctl_args *uap)
 {
-       struct proc *p = curproc;
+       struct thread *td = curthread;
        int semid = uap->semid;
        int semnum = uap->semnum;
        int cmd = uap->cmd;
        union semun *arg = uap->arg;
        union semun real_arg;
-       struct ucred *cred = p->p_ucred;
+       struct ucred *cred = td->td_ucred;
        int i, rval, eval;
        struct semid_ds sbuf;
        struct semid_ds *semaptr;
+       struct semid_ds *semakptr;
 
 #ifdef SEM_DEBUG
        kprintf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
 #endif
 
-       if (!jail_sysvipc_allowed && p->p_ucred->cr_prison != NULL)
+       if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
                return (ENOSYS);
 
+       switch (cmd) {
+       case SEM_STAT:
+               /*
+                * For this command we assume semid is an array index
+                * rather than an IPC id.
+                */
+               if (semid < 0 || semid >= seminfo.semmni) {
+                       eval = EINVAL;
+                       break;
+               }
+               semakptr = &sema[semid];
+               lwkt_getpooltoken(semakptr);
+               if ((semakptr->sem_perm.mode & SEM_ALLOC) == 0) {
+                       eval = EINVAL;
+                       lwkt_relpooltoken(semakptr);
+                       break;
+               }
+               if ((eval = ipcperm(td->td_proc, &semakptr->sem_perm, IPC_R))) {
+                       lwkt_relpooltoken(semakptr);
+                       break;
+               }
+               bcopy(&semakptr, arg->buf, sizeof(struct semid_ds));
+               rval = IXSEQ_TO_IPCID(semid, semakptr->sem_perm);
+               lwkt_relpooltoken(semakptr);
+               break;
+       }
+       
        semid = IPCID_TO_IX(semid);
-       if (semid < 0 || semid >= seminfo.semmni)
+       if (semid < 0 || semid >= seminfo.semmni) {
                return(EINVAL);
-
+       }
        semaptr = &sema[semid];
+       lwkt_getpooltoken(semaptr);
+
        if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
-           semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
+           semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
+               lwkt_relpooltoken(semaptr);
                return(EINVAL);
+       }
 
        eval = 0;
        rval = 0;
 
        switch (cmd) {
        case IPC_RMID:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_M)))
-                       return(eval);
+               if ((eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_M)) != 0)
+                       break;
                semaptr->sem_perm.cuid = cred->cr_uid;
                semaptr->sem_perm.uid = cred->cr_uid;
                semtot -= semaptr->sem_nsems;
@@ -424,95 +492,114 @@ sys___semctl(struct __semctl_args *uap)
                break;
 
        case IPC_SET:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_M)))
-                       return(eval);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_M);
+               if (eval)
+                       break;
                if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
-                       return(eval);
+                       break;
                if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf,
-                   sizeof(sbuf))) != 0)
-                       return(eval);
+                                  sizeof(sbuf))) != 0) {
+                       break;
+               }
                semaptr->sem_perm.uid = sbuf.sem_perm.uid;
                semaptr->sem_perm.gid = sbuf.sem_perm.gid;
                semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
-                   (sbuf.sem_perm.mode & 0777);
+                                        (sbuf.sem_perm.mode & 0777);
                semaptr->sem_ctime = time_second;
                break;
 
        case IPC_STAT:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
+               if ((eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R)))
+                       break;
                if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
-                       return(eval);
-               eval = copyout((caddr_t)semaptr, real_arg.buf,
-                   sizeof(struct semid_ds));
+                       break;
+               eval = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds));
                break;
 
        case GETNCNT:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
-               if (semnum < 0 || semnum >= semaptr->sem_nsems)
-                       return(EINVAL);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R);
+               if (eval)
+                       break;
+               if (semnum < 0 || semnum >= semaptr->sem_nsems) {
+                       eval = EINVAL;
+                       break;
+               }
                rval = semaptr->sem_base[semnum].semncnt;
                break;
 
        case GETPID:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
-               if (semnum < 0 || semnum >= semaptr->sem_nsems)
-                       return(EINVAL);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R);
+               if (eval)
+                       break;
+               if (semnum < 0 || semnum >= semaptr->sem_nsems) {
+                       eval = EINVAL;
+                       break;
+               }
                rval = semaptr->sem_base[semnum].sempid;
                break;
 
        case GETVAL:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
-               if (semnum < 0 || semnum >= semaptr->sem_nsems)
-                       return(EINVAL);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R);
+               if (eval)
+                       break;
+               if (semnum < 0 || semnum >= semaptr->sem_nsems) {
+                       eval = EINVAL;
+                       break;
+               }
                rval = semaptr->sem_base[semnum].semval;
                break;
 
        case GETALL:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R);
+               if (eval)
+                       break;
                if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
-                       return(eval);
+                       break;
                for (i = 0; i < semaptr->sem_nsems; i++) {
-                       eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
-                           &real_arg.array[i], sizeof(real_arg.array[0]));
-                       if (eval != 0)
+                       eval = copyout(&semaptr->sem_base[i].semval,
+                                      &real_arg.array[i],
+                                      sizeof(real_arg.array[0]));
+                       if (eval)
                                break;
                }
                break;
 
        case GETZCNT:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R)))
-                       return(eval);
-               if (semnum < 0 || semnum >= semaptr->sem_nsems)
-                       return(EINVAL);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_R);
+               if (eval)
+                       break;
+               if (semnum < 0 || semnum >= semaptr->sem_nsems) {
+                       eval = EINVAL;
+                       break;
+               }
                rval = semaptr->sem_base[semnum].semzcnt;
                break;
 
        case SETVAL:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W)))
-                       return(eval);
-               if (semnum < 0 || semnum >= semaptr->sem_nsems)
-                       return(EINVAL);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_W);
+               if (eval)
+                       break;
+               if (semnum < 0 || semnum >= semaptr->sem_nsems) {
+                       eval = EINVAL;
+                       break;
+               }
                if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
-                       return(eval);
+                       break;
                semaptr->sem_base[semnum].semval = real_arg.val;
                semundo_clear(semid, semnum);
                wakeup((caddr_t)semaptr);
                break;
 
        case SETALL:
-               if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W)))
-                       return(eval);
+               eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_W);
+               if (eval)
+                       break;
                if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
-                       return(eval);
+                       break;
                for (i = 0; i < semaptr->sem_nsems; i++) {
                        eval = copyin(&real_arg.array[i],
-                           (caddr_t)&semaptr->sem_base[i].semval,
-                           sizeof(real_arg.array[0]));
+                                     (caddr_t)&semaptr->sem_base[i].semval,
+                                     sizeof(real_arg.array[0]));
                        if (eval != 0)
                                break;
                }
@@ -521,57 +608,79 @@ sys___semctl(struct __semctl_args *uap)
                break;
 
        default:
-               return(EINVAL);
+               eval = EINVAL;
+               break;
        }
+       lwkt_relpooltoken(semaptr);
 
        if (eval == 0)
                uap->sysmsg_result = rval;
        return(eval);
 }
 
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_semget(struct semget_args *uap)
 {
-       struct proc *p = curproc;
+       struct thread *td = curthread;
        int semid, eval;
        int key = uap->key;
        int nsems = uap->nsems;
        int semflg = uap->semflg;
-       struct ucred *cred = p->p_ucred;
+       struct ucred *cred = td->td_ucred;
 
 #ifdef SEM_DEBUG
        kprintf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
 #endif
 
-       if (!jail_sysvipc_allowed && p->p_ucred->cr_prison != NULL)
+       if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
                return (ENOSYS);
 
+       eval = 0;
+
        if (key != IPC_PRIVATE) {
                for (semid = 0; semid < seminfo.semmni; semid++) {
-                       if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
-                           sema[semid].sem_perm.key == key)
-                               break;
+                       if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0 ||
+                           sema[semid].sem_perm.key != key) {
+                               continue;
+                       }
+                       lwkt_getpooltoken(&sema[semid]);
+                       if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0 ||
+                           sema[semid].sem_perm.key != key) {
+                               lwkt_relpooltoken(&sema[semid]);
+                               continue;
+                       }
+                       break;
                }
                if (semid < seminfo.semmni) {
 #ifdef SEM_DEBUG
                        kprintf("found public key\n");
 #endif
-                       if ((eval = ipcperm(p, &sema[semid].sem_perm,
-                           semflg & 0700)))
-                               return(eval);
+                       if ((eval = ipcperm(td->td_proc,
+                                           &sema[semid].sem_perm,
+                                           semflg & 0700))) {
+                               lwkt_relpooltoken(&sema[semid]);
+                               goto done;
+                       }
                        if (nsems > 0 && sema[semid].sem_nsems < nsems) {
 #ifdef SEM_DEBUG
                                kprintf("too small\n");
 #endif
-                               return(EINVAL);
+                               eval = EINVAL;
+                               lwkt_relpooltoken(&sema[semid]);
+                               goto done;
                        }
                        if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
 #ifdef SEM_DEBUG
                                kprintf("not exclusive\n");
 #endif
-                               return(EEXIST);
+                               eval = EEXIST;
+                               lwkt_relpooltoken(&sema[semid]);
+                               goto done;
                        }
-                       goto found;
+                       goto done;
                }
        }
 
@@ -581,27 +690,37 @@ sys_semget(struct semget_args *uap)
        if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
                if (nsems <= 0 || nsems > seminfo.semmsl) {
 #ifdef SEM_DEBUG
-                       kprintf("nsems out of range (0<%d<=%d)\n", nsems,
-                           seminfo.semmsl);
+                       kprintf("nsems out of range (0<%d<=%d)\n",
+                               nsems, seminfo.semmsl);
 #endif
-                       return(EINVAL);
+                       eval = EINVAL;
+                       goto done;
                }
                if (nsems > seminfo.semmns - semtot) {
 #ifdef SEM_DEBUG
-                       kprintf("not enough semaphores left (need %d, got %d)\n",
-                           nsems, seminfo.semmns - semtot);
+                       kprintf("not enough semaphores left "
+                               "(need %d, got %d)\n",
+                               nsems, seminfo.semmns - semtot);
 #endif
-                       return(ENOSPC);
+                       eval = ENOSPC;
+                       goto done;
                }
                for (semid = 0; semid < seminfo.semmni; semid++) {
-                       if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
-                               break;
+                       if (sema[semid].sem_perm.mode & SEM_ALLOC)
+                               continue;
+                       lwkt_getpooltoken(&sema[semid]);
+                       if (sema[semid].sem_perm.mode & SEM_ALLOC) {
+                               lwkt_relpooltoken(&sema[semid]);
+                               continue;
+                       }
+                       break;
                }
                if (semid == seminfo.semmni) {
 #ifdef SEM_DEBUG
                        kprintf("no more semid_ds's available\n");
 #endif
-                       return(ENOSPC);
+                       eval = ENOSPC;
+                       goto done;
                }
 #ifdef SEM_DEBUG
                kprintf("semid %d is available\n", semid);
@@ -622,25 +741,33 @@ sys_semget(struct semget_args *uap)
                bzero(sema[semid].sem_base,
                    sizeof(sema[semid].sem_base[0])*nsems);
 #ifdef SEM_DEBUG
-               kprintf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
-                   &sem[semtot]);
+               kprintf("sembase = 0x%x, next = 0x%x\n",
+                       sema[semid].sem_base, &sem[semtot]);
 #endif
+               /* eval == 0 */
        } else {
 #ifdef SEM_DEBUG
                kprintf("didn't find it and wasn't asked to create it\n");
 #endif
-               return(ENOENT);
+               eval = ENOENT;
        }
 
-found:
-       uap->sysmsg_result = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
-       return(0);
+done:
+       if (eval == 0) {
+               uap->sysmsg_result =
+                       IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
+               lwkt_relpooltoken(&sema[semid]);
+       }
+       return(eval);
 }
 
+/*
+ * MPALMOSTSAFE
+ */
 int
 sys_semop(struct semop_args *uap)
 {
-       struct proc *p = curproc;
+       struct thread *td = curthread;
        int semid = uap->semid;
        u_int nsops = uap->nsops;
        struct sembuf sops[MAX_SOPS];
@@ -655,32 +782,39 @@ sys_semop(struct semop_args *uap)
        kprintf("call to semop(%d, 0x%x, %u)\n", semid, sops, nsops);
 #endif
 
-       if (!jail_sysvipc_allowed && p->p_ucred->cr_prison != NULL)
+       if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
                return (ENOSYS);
 
        semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
 
-       if (semid < 0 || semid >= seminfo.semmni)
-               return(EINVAL);
-
+       if (semid < 0 || semid >= seminfo.semmni) {
+               eval = EINVAL;
+               goto done2;
+       }
        semaptr = &sema[semid];
-       if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
-               return(EINVAL);
-       if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
-               return(EINVAL);
+       lwkt_getpooltoken(semaptr);
+       if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) {
+               eval = EINVAL;
+               goto done;
+       }
+       if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
+               eval = EINVAL;
+               goto done;
+       }
 
-       if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W))) {
+       if ((eval = ipcperm(td->td_proc, &semaptr->sem_perm, IPC_W))) {
 #ifdef SEM_DEBUG
                kprintf("eval = %d from ipaccess\n", eval);
 #endif
-               return(eval);
+               goto done;
        }
 
        if (nsops > MAX_SOPS) {
 #ifdef SEM_DEBUG
                kprintf("too many sops (max=%d, nsops=%u)\n", MAX_SOPS, nsops);
 #endif
-               return(E2BIG);
+               eval = E2BIG;
+               goto done;
        }
 
        if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
@@ -688,7 +822,7 @@ sys_semop(struct semop_args *uap)
                kprintf("eval = %d from copyin(%08x, %08x, %u)\n", eval,
                    uap->sops, &sops, nsops * sizeof(sops[0]));
 #endif
-               return(eval);
+               goto done;
        }
 
        /*
@@ -708,8 +842,10 @@ sys_semop(struct semop_args *uap)
                for (i = 0; i < nsops; i++) {
                        sopptr = &sops[i];
 
-                       if (sopptr->sem_num >= semaptr->sem_nsems)
-                               return(EFBIG);
+                       if (sopptr->sem_num >= semaptr->sem_nsems) {
+                               eval = EFBIG;
+                               goto done;
+                       }
 
                        semptr = &semaptr->sem_base[sopptr->sem_num];
 
@@ -754,7 +890,7 @@ sys_semop(struct semop_args *uap)
                 * Did we get through the entire vector?
                 */
                if (i >= nsops)
-                       goto done;
+                       goto donex;
 
                /*
                 * No ... rollback anything that we've already done
@@ -770,8 +906,10 @@ sys_semop(struct semop_args *uap)
                 * If the request that we couldn't satisfy has the
                 * NOWAIT flag set then return with EAGAIN.
                 */
-               if (sopptr->sem_flg & IPC_NOWAIT)
-                       return(EAGAIN);
+               if (sopptr->sem_flg & IPC_NOWAIT) {
+                       eval = EAGAIN;
+                       goto done;
+               }
 
                if (sopptr->sem_op == 0)
                        semptr->semzcnt++;
@@ -794,8 +932,10 @@ sys_semop(struct semop_args *uap)
                 * Make sure that the semaphore still exists
                 */
                if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
-                   semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
-                       return(EIDRM);
+                   semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
+                       eval = EIDRM;
+                       goto done;
+               }
 
                /*
                 * The semaphore is still alive.  Readjust the count of
@@ -811,14 +951,16 @@ sys_semop(struct semop_args *uap)
                 * (Delayed check of tsleep() return code because we
                 * need to decrement sem[nz]cnt either way.)
                 */
-               if (eval != 0)
-                       return(EINTR);
+               if (eval) {
+                       eval = EINTR;
+                       goto done;
+               }
 #ifdef SEM_DEBUG
                kprintf("semop:  good morning!\n");
 #endif
        }
 
-done:
+donex:
        /*
         * Process any SEM_UNDO requests.
         */
@@ -835,8 +977,8 @@ done:
                        adjval = sops[i].sem_op;
                        if (adjval == 0)
                                continue;
-                       eval = semundo_adjust(p, &suptr, semid,
-                           sops[i].sem_num, -adjval);
+                       eval = semundo_adjust(td->td_proc, &suptr, semid,
+                                             sops[i].sem_num, -adjval);
                        if (eval == 0)
                                continue;
 
@@ -855,8 +997,8 @@ done:
                                adjval = sops[j].sem_op;
                                if (adjval == 0)
                                        continue;
-                               if (semundo_adjust(p, &suptr, semid,
-                                   sops[j].sem_num, adjval) != 0)
+                               if (semundo_adjust(td->td_proc, &suptr, semid,
+                                              sops[j].sem_num, adjval) != 0)
                                        panic("semop - can't undo undos");
                        }
 
@@ -867,7 +1009,7 @@ done:
 #ifdef SEM_DEBUG
                        kprintf("eval = %d from semundo_adjust\n", eval);
 #endif
-                       return(eval);
+                       goto done;
                } /* loop through the sops */
        } /* if (do_undos) */
 
@@ -875,7 +1017,7 @@ done:
        for (i = 0; i < nsops; i++) {
                sopptr = &sops[i];
                semptr = &semaptr->sem_base[sopptr->sem_num];
-               semptr->sempid = p->p_pid;
+               semptr->sempid = td->td_proc->p_pid;
        }
 
        /* Do a wakeup if any semaphore was up'd. */
@@ -892,7 +1034,11 @@ done:
        kprintf("semop:  done\n");
 #endif
        uap->sysmsg_result = 0;
-       return(0);
+       eval = 0;
+done:
+       lwkt_relpooltoken(semaptr);
+done2:
+       return(eval);
 }
 
 /*
@@ -909,22 +1055,35 @@ semexit(struct proc *p)
        did_something = 0;
 
        /*
-        * Go through the chain of undo vectors looking for one
-        * associated with this process.
+        * We're getting a global token, don't do it if we couldn't
+        * possibly have any semaphores.
         */
+       if ((p->p_flags & P_SYSVSEM) == 0)
+               return;
 
+       /*
+        * Go through the chain of undo vectors looking for one
+        * associated with this process.  De-link it from the
+        * list right now, while we have the token, but do not
+        * clear un_proc until we finish cleaning up the relationship.
+        */
+       lwkt_gettoken(&semu_token);
        for (supptr = &semu_list; (suptr = *supptr) != NULL;
-           supptr = &suptr->un_next) {
-               if (suptr->un_proc == p)
+            supptr = &suptr->un_next) {
+               if (suptr->un_proc == p) {
+                       *supptr = suptr->un_next;
                        break;
+               }
        }
+       p->p_flags &= ~P_SYSVSEM;
+       lwkt_reltoken(&semu_token);
 
        if (suptr == NULL)
                return;
 
 #ifdef SEM_DEBUG
        kprintf("proc @%08x has undo structure with %d entries\n", p,
-           suptr->un_cnt);
+               suptr->un_cnt);
 #endif
 
        /*
@@ -972,9 +1131,9 @@ semexit(struct proc *p)
        /*
         * Deallocate the undo vector.
         */
+       cpu_sfence();
+       suptr->un_proc = NULL;
 #ifdef SEM_DEBUG
        kprintf("removing vector\n");
 #endif
-       suptr->un_proc = NULL;
-       *supptr = suptr->un_next;
 }