diff options
Diffstat (limited to 'sys/kern/sysv_sem.c')
| -rw-r--r-- | sys/kern/sysv_sem.c | 660 |
1 files changed, 369 insertions, 291 deletions
diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c index 60290511b3b..e84b43a36cb 100644 --- a/sys/kern/sysv_sem.c +++ b/sys/kern/sysv_sem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sysv_sem.c,v 1.9 2002/03/14 01:27:05 millert Exp $ */ +/* $OpenBSD: sysv_sem.c,v 1.10 2002/12/17 23:11:31 millert Exp $ */ /* $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $ */ /* @@ -14,39 +14,44 @@ #include <sys/kernel.h> #include <sys/proc.h> #include <sys/sem.h> +#include <sys/sysctl.h> #include <sys/malloc.h> +#include <sys/pool.h> #include <sys/mount.h> #include <sys/syscallargs.h> +/* SVID defines EIDRM but BSD does not */ +#ifndef EIDRM +#define EIDRM EINVAL +#endif + int semtot = 0; -struct semid_ds *sema; /* semaphore id pool */ -struct sem *sem; /* semaphore pool */ -struct sem_undo *semu_list; /* list of active undo structures */ -int *semu; /* undo structure pool */ +int semutot = 0; +struct semid_ds **sema; /* semaphore id list */ +struct sem_undo *semu_list; /* list of undo structures */ +struct pool sema_pool; /* pool for struct semid_ds */ +struct pool semu_pool; /* pool for struct sem_undo (SEMUSZ) */ +unsigned short *semseqs; /* array of sem sequence numbers */ struct sem_undo *semu_alloc(struct proc *); int semundo_adjust(struct proc *, struct sem_undo **, int, int, int); void semundo_clear(int, int); void -seminit() +seminit(void) { - register int i; - if (sema == NULL) - panic("sema is NULL"); - if (semu == NULL) - panic("semu is NULL"); - - for (i = 0; i < seminfo.semmni; i++) { - sema[i].sem_base = 0; - sema[i].sem_perm.mode = 0; - } - for (i = 0; i < seminfo.semmnu; i++) { - register struct sem_undo *suptr = SEMU(i); - suptr->un_proc = NULL; - } + pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl", + &pool_allocator_nointr); + pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl", + &pool_allocator_nointr); + sema = malloc(seminfo.semmni * sizeof(struct semid_ds *), + M_SEM, M_WAITOK); + bzero(sema, seminfo.semmni * sizeof(struct semid_ds *)); + semseqs = malloc(seminfo.semmni * sizeof(unsigned short), + M_SEM, M_WAITOK); + bzero(semseqs, seminfo.semmni * sizeof(unsigned short)); semu_list = NULL; } @@ -55,88 +60,36 @@ seminit() * (returns ptr to structure or NULL if no more room) */ struct sem_undo * -semu_alloc(p) - struct proc *p; +semu_alloc(struct proc *p) { - register int i; - register struct sem_undo *suptr; - register struct sem_undo **supptr; - int attempt; - - /* - * Try twice to allocate something. - * (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) { - suptr->un_next = semu_list; - semu_list = suptr; - suptr->un_cnt = 0; - suptr->un_proc = p; - return(suptr); - } - } - - /* - * We didn't find a free one, if this is the first attempt - * then try to free some structures. - */ - - if (attempt == 0) { - /* All the structures are in use - try to free some */ - int did_something = 0; - - supptr = &semu_list; - while ((suptr = *supptr) != NULL) { - if (suptr->un_cnt == 0) { - suptr->un_proc = NULL; - *supptr = suptr->un_next; - did_something = 1; - } else - supptr = &(suptr->un_next); - } - - /* If we didn't free anything then just give-up */ - if (!did_something) - return(NULL); - } else { - /* - * The second pass failed even though we freed - * something after the first pass! - * This is IMPOSSIBLE! - */ - panic("semu_alloc - second attempt failed"); - } - } - return NULL; + struct sem_undo *suptr; + + if (semutot == seminfo.semmnu || + (suptr = pool_get(&semu_pool, 0)) == NULL) + return (NULL); /* no space */ + + semutot++; + suptr->un_cnt = 0; + suptr->un_proc = p; + suptr->un_next = semu_list; + semu_list = suptr; + return (suptr); } /* * Adjust a particular entry for a particular proc */ int -semundo_adjust(p, supptr, semid, semnum, adjval) - register struct proc *p; - struct sem_undo **supptr; - int semid, semnum; - int adjval; +semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum, + int adjval) { - register struct sem_undo *suptr; - register struct undo *sunptr; + struct sem_undo *suptr; + struct undo *sunptr; 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) { for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { @@ -147,10 +100,10 @@ semundo_adjust(p, supptr, semid, semnum, adjval) } if (suptr == NULL) { if (adjval == 0) - return(0); + return (0); suptr = semu_alloc(p); if (suptr == NULL) - return(ENOSPC); + return (ENOSPC); *supptr = suptr; } } @@ -167,39 +120,56 @@ semundo_adjust(p, supptr, semid, semnum, adjval) sunptr->un_adjval = 0; else sunptr->un_adjval += adjval; - if (sunptr->un_adjval == 0) { - suptr->un_cnt--; - if (i < suptr->un_cnt) - suptr->un_ent[i] = - suptr->un_ent[suptr->un_cnt]; - } - return(0); + if (sunptr->un_adjval != 0) + return (0); + + if (--suptr->un_cnt == 0) { + struct sem_undo *suprev; + + if (semu_list == suptr) + semu_list = suptr->un_next; + else { + /* this code path should be rare */ + for (suprev = semu_list; suprev != NULL && + suprev->un_next != suptr; + suprev = suprev->un_next) + /* NOTHING */; +#ifdef DIAGNOSTIC + if (suprev == NULL) + panic("semundo_adjust: " + "suptr not in semu_list"); +#endif + suprev->un_next = suptr->un_next; + } + pool_put(&semu_pool, suptr); + } else if (i < suptr->un_cnt) + suptr->un_ent[i] = + suptr->un_ent[suptr->un_cnt]; + return (0); } /* Didn't find the right entry - create it */ if (adjval == 0) - return(0); + return (0); if (suptr->un_cnt == SEMUME) - return(EINVAL); + return (EINVAL); sunptr = &suptr->un_ent[suptr->un_cnt]; suptr->un_cnt++; sunptr->un_adjval = adjval; sunptr->un_id = semid; sunptr->un_num = semnum; - return(0); + return (0); } void -semundo_clear(semid, semnum) - int semid, semnum; +semundo_clear(int semid, int semnum) { - register struct sem_undo *suptr; - - for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { - register struct undo *sunptr; - register int i; + struct sem_undo *suptr, *suprev, *tmp; + struct undo *sunptr; + int i; + for (suptr = semu_list; suptr != NULL; ) { sunptr = &suptr->un_ent[0]; for (i = 0; i < suptr->un_cnt; i++, sunptr++) { if (sunptr->un_id == semid) { @@ -215,13 +185,22 @@ semundo_clear(semid, semnum) break; } } + if (suptr->un_cnt == 0) { + tmp = suptr; + if (suptr == semu_list) + suptr = semu_list = suptr->un_next; + else + suptr = suprev->un_next = suptr->un_next; + pool_put(&semu_pool, tmp); + } else { + suprev = suptr; + suptr = suptr->un_next; + } } } void -semid_n2o(n, o) - struct semid_ds *n; - struct osemid_ds *o; +semid_n2o(struct semid_ds *n, struct osemid_ds *o) { o->sem_base = n->sem_base; o->sem_nsems = n->sem_nsems; @@ -234,12 +213,9 @@ semid_n2o(n, o) } int -sys___semctl(p, v, retval) - struct proc *p; - register void *v; - register_t *retval; +sys___semctl(struct proc *p, void *v, register_t *retval) { - register struct sys___semctl_args /* { + struct sys___semctl_args /* { syscallarg(int) semid; syscallarg(int) semnum; syscallarg(int) cmd; @@ -251,9 +227,9 @@ sys___semctl(p, v, retval) union semun *arg = SCARG(uap, arg); union semun real_arg; struct ucred *cred = p->p_ucred; - int i, rval, eval; + int i, rval, error; struct semid_ds sbuf; - register struct semid_ds *semaptr; + struct semid_ds *semaptr; #ifdef SEM_DEBUG printf("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg); @@ -261,43 +237,36 @@ sys___semctl(p, v, retval) semid = IPCID_TO_IX(semid); if (semid < 0 || semid >= seminfo.semmsl) - return(EINVAL); + return (EINVAL); - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || + if ((semaptr = sema[semid]) == NULL || semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) - return(EINVAL); + return (EINVAL); - eval = 0; - rval = 0; + error = rval = 0; switch (cmd) { case IPC_RMID: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) + return (error); semaptr->sem_perm.cuid = cred->cr_uid; semaptr->sem_perm.uid = cred->cr_uid; semtot -= semaptr->sem_nsems; - for (i = semaptr->sem_base - sem; i < semtot; i++) - sem[i] = sem[i + semaptr->sem_nsems]; - for (i = 0; i < seminfo.semmni; i++) { - if ((sema[i].sem_perm.mode & SEM_ALLOC) && - sema[i].sem_base > semaptr->sem_base) - sema[i].sem_base -= semaptr->sem_nsems; - } - semaptr->sem_perm.mode = 0; + free(semaptr->sem_base, M_SEM); + pool_put(&sema_pool, semaptr); + sema[semid] = NULL; semundo_clear(semid, -1); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)&sema[semid]); break; case IPC_SET: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) - return(eval); - if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) - return(eval); - if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf, + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M))) + return (error); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); + if ((error = copyin(real_arg.buf, (caddr_t)&sbuf, sizeof(sbuf))) != 0) - return(eval); + return (error); 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) | @@ -306,141 +275,153 @@ sys___semctl(p, v, retval) break; case IPC_STAT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); - if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) - return(eval); - eval = copyout((caddr_t)semaptr, real_arg.buf, + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); + error = copyout((caddr_t)semaptr, real_arg.buf, sizeof(struct semid_ds)); break; case GETNCNT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); if (semnum < 0 || semnum >= semaptr->sem_nsems) - return(EINVAL); + return (EINVAL); rval = semaptr->sem_base[semnum].semncnt; break; case GETPID: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); if (semnum < 0 || semnum >= semaptr->sem_nsems) - return(EINVAL); + return (EINVAL); rval = semaptr->sem_base[semnum].sempid; break; case GETVAL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); if (semnum < 0 || semnum >= semaptr->sem_nsems) - return(EINVAL); + return (EINVAL); rval = semaptr->sem_base[semnum].semval; break; case GETALL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); - if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); for (i = 0; i < semaptr->sem_nsems; i++) { - eval = copyout((caddr_t)&semaptr->sem_base[i].semval, + error = copyout((caddr_t)&semaptr->sem_base[i].semval, &real_arg.array[i], sizeof(real_arg.array[0])); - if (eval != 0) + if (error != 0) break; } break; case GETZCNT: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) + return (error); if (semnum < 0 || semnum >= semaptr->sem_nsems) - return(EINVAL); + return (EINVAL); rval = semaptr->sem_base[semnum].semzcnt; break; case SETVAL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) + return (error); if (semnum < 0 || semnum >= semaptr->sem_nsems) - return(EINVAL); - if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) - return(eval); + return (EINVAL); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); semaptr->sem_base[semnum].semval = real_arg.val; semundo_clear(semid, semnum); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)&sema[semid]); break; case SETALL: - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) - return(eval); - if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) - return(eval); + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) + return (error); + if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) + return (error); for (i = 0; i < semaptr->sem_nsems; i++) { - eval = copyin(&real_arg.array[i], + error = copyin(&real_arg.array[i], (caddr_t)&semaptr->sem_base[i].semval, sizeof(real_arg.array[0])); - if (eval != 0) + if (error != 0) break; } semundo_clear(semid, -1); - wakeup((caddr_t)semaptr); + wakeup((caddr_t)&sema[semid]); break; default: - return(EINVAL); + return (EINVAL); } - if (eval == 0) + if (error == 0) *retval = rval; - return(eval); + return (error); } int -sys_semget(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_semget(struct proc *p, void *v, register_t *retval) { - register struct sys_semget_args /* { + struct sys_semget_args /* { syscallarg(key_t) key; syscallarg(int) nsems; syscallarg(int) semflg; } */ *uap = v; - int semid, eval; + int semid, error; int key = SCARG(uap, key); int nsems = SCARG(uap, nsems); int semflg = SCARG(uap, semflg); + struct semid_ds *semaptr, *semaptr_new = NULL; struct ucred *cred = p->p_ucred; #ifdef SEM_DEBUG printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); #endif + /* + * Preallocate space for the new semaphore. If we are going + * to sleep, we want to sleep now to elliminate any race + * condition in allocating a semaphore with a specific key. + */ + if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { + semaptr_new = pool_get(&sema_pool, PR_WAITOK); + semaptr_new->sem_base = malloc(nsems * sizeof(struct sem), + M_SEM, M_WAITOK); + bzero(semaptr_new->sem_base, nsems * sizeof(struct sem)); + } + 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) + for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) { + if ((semaptr = sema[semid]) != NULL && + semaptr->sem_perm.key == key) break; } - if (semid < seminfo.semmni) { + if (semaptr != NULL) { #ifdef SEM_DEBUG printf("found public key\n"); #endif - if ((eval = ipcperm(cred, &sema[semid].sem_perm, + if ((error = ipcperm(cred, &semaptr->sem_perm, semflg & 0700))) - return(eval); - if (nsems > 0 && sema[semid].sem_nsems < nsems) { + goto error; + if (nsems > 0 && semaptr->sem_nsems < nsems) { #ifdef SEM_DEBUG printf("too small\n"); #endif - return(EINVAL); + error = EINVAL; + goto error; } if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { #ifdef SEM_DEBUG printf("not exclusive\n"); #endif - return(EEXIST); + error = EEXIST; + goto error; } goto found; } @@ -455,79 +436,78 @@ sys_semget(p, v, retval) printf("nsems out of range (0<%d<=%d)\n", nsems, seminfo.semmsl); #endif - return(EINVAL); + error = EINVAL; + goto error; } if (nsems > seminfo.semmns - semtot) { #ifdef SEM_DEBUG printf("not enough semaphores left (need %d, got %d)\n", nsems, seminfo.semmns - semtot); #endif - return(ENOSPC); + error = ENOSPC; + goto error; } for (semid = 0; semid < seminfo.semmni; semid++) { - if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) + if ((semaptr = sema[semid]) == NULL) break; } if (semid == seminfo.semmni) { #ifdef SEM_DEBUG printf("no more semid_ds's available\n"); #endif - return(ENOSPC); + error = ENOSPC; + goto error; } #ifdef SEM_DEBUG printf("semid %d is available\n", semid); #endif - sema[semid].sem_perm.key = key; - sema[semid].sem_perm.cuid = cred->cr_uid; - sema[semid].sem_perm.uid = cred->cr_uid; - sema[semid].sem_perm.cgid = cred->cr_gid; - sema[semid].sem_perm.gid = cred->cr_gid; - sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; - sema[semid].sem_perm.seq = - (sema[semid].sem_perm.seq + 1) & 0x7fff; - sema[semid].sem_nsems = nsems; - sema[semid].sem_otime = 0; - sema[semid].sem_ctime = time.tv_sec; - sema[semid].sem_base = &sem[semtot]; - semtot += nsems; - bzero(sema[semid].sem_base, - sizeof(sema[semid].sem_base[0])*nsems); -#ifdef SEM_DEBUG - printf("sembase = %p, next = %p\n", sema[semid].sem_base, - &sem[semtot]); -#endif + semaptr_new->sem_perm.key = key; + semaptr_new->sem_perm.cuid = cred->cr_uid; + semaptr_new->sem_perm.uid = cred->cr_uid; + semaptr_new->sem_perm.cgid = cred->cr_gid; + semaptr_new->sem_perm.gid = cred->cr_gid; + semaptr_new->sem_perm.mode = (semflg & 0777); + semaptr_new->sem_perm.seq = semseqs[semid] = + (semseqs[semid] + 1) & 0x7fff; + semaptr_new->sem_nsems = nsems; + semaptr_new->sem_otime = 0; + semaptr_new->sem_ctime = time.tv_sec; + sema[semid] = semaptr_new; } else { #ifdef SEM_DEBUG printf("didn't find it and wasn't asked to create it\n"); #endif - return(ENOENT); + return (ENOENT); } found: - *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); - return(0); + *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm); + return (0); +error: + if (semaptr_new != NULL) { + free(semaptr_new->sem_base, M_SEM); + pool_put(&sema_pool, semaptr_new); + } + return (error); } int -sys_semop(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_semop(struct proc *p, void *v, register_t *retval) { - register struct sys_semop_args /* { + struct sys_semop_args /* { syscallarg(int) semid; syscallarg(struct sembuf *) sops; syscallarg(u_int) nsops; } */ *uap = v; int semid = SCARG(uap, semid); u_int nsops = SCARG(uap, nsops); - struct sembuf sops[MAX_SOPS]; - register struct semid_ds *semaptr; - register struct sembuf *sopptr = NULL; - register struct sem *semptr = NULL; + struct sembuf *sops; + struct semid_ds *semaptr; + struct sembuf *sopptr = NULL; + struct sem *semptr = NULL; struct sem_undo *suptr = NULL; struct ucred *cred = p->p_ucred; - int i, j, eval; + int i, j, error; int do_wakeup, do_undos; #ifdef SEM_DEBUG @@ -537,34 +517,35 @@ sys_semop(p, v, retval) semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ if (semid < 0 || semid >= seminfo.semmsl) - return(EINVAL); + return (EINVAL); - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || + if ((semaptr = sema[semid]) == NULL || semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) - return(EINVAL); + return (EINVAL); - if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { + if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { #ifdef SEM_DEBUG - printf("eval = %d from ipaccess\n", eval); + printf("error = %d from ipaccess\n", error); #endif - return(eval); + return (error); } - if (nsops > MAX_SOPS) { + if (nsops > seminfo.semopm) { #ifdef SEM_DEBUG - printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); + printf("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, nsops); #endif - return(E2BIG); + return (E2BIG); } - if ((eval = copyin(SCARG(uap, sops), sops, nsops * sizeof(sops[0]))) - != 0) { + sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK); + error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf)); + if (error != 0) { #ifdef SEM_DEBUG - printf("eval = %d from copyin(%p, %p, %d)\n", eval, - SCARG(uap, sops), &sops, nsops * sizeof(sops[0])); + printf("error = %d from copyin(%p, %p, %d)\n", error, + SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)); #endif - return(eval); + free(sops, M_SEM); + return (error); } /* @@ -584,8 +565,10 @@ sys_semop(p, v, retval) 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) { + free(sops, M_SEM); + return (EFBIG); + } semptr = &semaptr->sem_base[sopptr->sem_num]; @@ -647,8 +630,10 @@ sys_semop(p, v, retval) * 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) { + free(sops, M_SEM); + return (EAGAIN); + } if (sopptr->sem_op == 0) semptr->semzcnt++; @@ -658,16 +643,18 @@ sys_semop(p, v, retval) #ifdef SEM_DEBUG printf("semop: good night!\n"); #endif - eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, + error = tsleep((caddr_t)&sema[semid], (PZERO - 4) | PCATCH, "semwait", 0); #ifdef SEM_DEBUG - printf("semop: good morning (eval=%d)!\n", eval); + printf("semop: good morning (error=%d)!\n", error); #endif suptr = NULL; /* sem_undo may have been reallocated */ - if (eval != 0) - return(EINTR); + if (error != 0) { + free(sops, M_SEM); + return (EINTR); + } #ifdef SEM_DEBUG printf("semop: good morning!\n"); #endif @@ -675,15 +662,10 @@ sys_semop(p, v, retval) /* * Make sure that the semaphore still exists */ - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || + if (sema[semid] == NULL || semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) { - /* The man page says to return EIDRM. */ - /* Unfortunately, BSD doesn't define that code! */ -#ifdef EIDRM - return(EIDRM); -#else - return(EINVAL); -#endif + free(sops, M_SEM); + return (EIDRM); } /* @@ -713,13 +695,13 @@ done: adjval = sops[i].sem_op; if (adjval == 0) continue; - eval = semundo_adjust(p, &suptr, semid, + error = semundo_adjust(p, &suptr, semid, sops[i].sem_num, -adjval); - if (eval == 0) + if (error == 0) continue; /* - * Oh-Oh! We ran out of either sem_undo's or undo's. + * Uh-Oh! We ran out of either sem_undo's or undo's. * Rollback the adjustments to this point and then * rollback the semaphore ups and down so we can return * with an error with all structures restored. We @@ -743,9 +725,10 @@ done: sops[j].sem_op; #ifdef SEM_DEBUG - printf("eval = %d from semundo_adjust\n", eval); + printf("error = %d from semundo_adjust\n", error); #endif - return(eval); + free(sops, M_SEM); + return (error); } /* loop through the sops */ } /* if (do_undos) */ @@ -755,26 +738,23 @@ done: semptr = &semaptr->sem_base[sopptr->sem_num]; semptr->sempid = p->p_pid; } + free(sops, M_SEM); /* Do a wakeup if any semaphore was up'd. */ if (do_wakeup) { #ifdef SEM_DEBUG printf("semop: doing wakeup\n"); -#ifdef SEM_WAKEUP - sem_wakeup((caddr_t)semaptr); -#else - wakeup((caddr_t)semaptr); #endif + wakeup((caddr_t)&sema[semid]); +#ifdef SEM_DEBUG printf("semop: back from wakeup\n"); -#else - wakeup((caddr_t)semaptr); #endif } #ifdef SEM_DEBUG printf("semop: done\n"); #endif *retval = 0; - return(0); + return (0); } /* @@ -782,17 +762,15 @@ done: * semaphores. */ void -semexit(p) - struct proc *p; +semexit(struct proc *p) { - register struct sem_undo *suptr; - register struct sem_undo **supptr; + struct sem_undo *suptr; + struct sem_undo **supptr; /* * Go through the chain of undo vectors looking for one associated with * this process. */ - for (supptr = &semu_list; (suptr = *supptr) != NULL; supptr = &suptr->un_next) { if (suptr->un_proc == p) @@ -812,7 +790,6 @@ semexit(p) * We are now in case 1 or 2, and we have an undo vector for this * process. */ - #ifdef SEM_DEBUG printf("proc @%p has undo structure with %d entries\n", p, suptr->un_cnt); @@ -830,8 +807,7 @@ semexit(p) int adjval = suptr->un_ent[ix].un_adjval; struct semid_ds *semaptr; - semaptr = &sema[semid]; - if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) + if ((semaptr = sema[semid]) == NULL) panic("semexit - semid not allocated"); if (semnum >= semaptr->sem_nsems) panic("semexit - semnum out of range"); @@ -850,11 +826,7 @@ semexit(p) else semaptr->sem_base[semnum].semval += adjval; -#ifdef SEM_WAKEUP - sem_wakeup((caddr_t)semaptr); -#else - wakeup((caddr_t)semaptr); -#endif + wakeup((caddr_t)&sema[semid]); #ifdef SEM_DEBUG printf("semexit: back from wakeup\n"); #endif @@ -867,6 +839,112 @@ semexit(p) #ifdef SEM_DEBUG printf("removing vector\n"); #endif - suptr->un_proc = NULL; *supptr = suptr->un_next; + pool_put(&semu_pool, suptr); +} + +/* + * Userland access to struct seminfo. + */ +int +sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int error, val; + struct semid_ds **sema_new; + unsigned short *newseqs; + + if (namelen != 2) { + switch (name[0]) { + case KERN_SEMINFO_SEMMNI: + case KERN_SEMINFO_SEMMNS: + case KERN_SEMINFO_SEMMNU: + case KERN_SEMINFO_SEMMSL: + case KERN_SEMINFO_SEMOPM: + case KERN_SEMINFO_SEMUME: + case KERN_SEMINFO_SEMUSZ: + case KERN_SEMINFO_SEMVMX: + case KERN_SEMINFO_SEMAEM: + break; + default: + return (ENOTDIR); /* overloaded */ + } + } + + switch (name[0]) { + case KERN_SEMINFO_SEMMNI: + val = seminfo.semmni; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || + val == seminfo.semmni) + return (error); + + if (val < seminfo.semmni) + return (EINVAL); /* can't decrease semmni */ + + /* Expand semsegs and semseqs arrays */ + sema_new = malloc(val * sizeof(struct semid_ds *), + M_SEM, M_WAITOK); + bcopy(sema, sema_new, + seminfo.semmni * sizeof(struct semid_ds *)); + bzero(sema_new + seminfo.semmni, + (val - seminfo.semmni) * sizeof(struct semid_ds *)); + newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK); + bcopy(semseqs, newseqs, + seminfo.semmni * sizeof(unsigned short)); + bzero(newseqs + seminfo.semmni, + (val - seminfo.semmni) * sizeof(unsigned short)); + free(sema, M_SEM); + free(semseqs, M_SEM); + sema = sema_new; + semseqs = newseqs; + seminfo.semmni = val; + return (0); + case KERN_SEMINFO_SEMMNS: + val = seminfo.semmns; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || + val == seminfo.semmns) + return (error); + if (val < seminfo.semmns) + return (EINVAL); /* can't decrease semmns */ + seminfo.semmns = val; + return (0); + case KERN_SEMINFO_SEMMNU: + val = seminfo.semmnu; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || + val == seminfo.semmnu) + return (error); + if (val < seminfo.semmnu) + return (EINVAL); /* can't decrease semmnu */ + seminfo.semmnu = val; + return (0); + case KERN_SEMINFO_SEMMSL: + val = seminfo.semmsl; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || + val == seminfo.semmsl) + return (error); + if (val < seminfo.semmsl) + return (EINVAL); /* can't decrease semmsl */ + seminfo.semmsl = val; + return (0); + case KERN_SEMINFO_SEMOPM: + val = seminfo.semopm; + if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) || + val == seminfo.semopm) + return (error); + if (val <= 0) + return (EINVAL); /* semopm must be >= 1 */ + seminfo.semopm = val; + return (0); + case KERN_SEMINFO_SEMUME: + return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume)); + case KERN_SEMINFO_SEMUSZ: + return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz)); + case KERN_SEMINFO_SEMVMX: + return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx)); + case KERN_SEMINFO_SEMAEM: + return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem)); + default: + return (EOPNOTSUPP); + } + /* NOTREACHED */ } |
