summaryrefslogtreecommitdiffstats
path: root/sys/kern/sysv_sem.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/sysv_sem.c')
-rw-r--r--sys/kern/sysv_sem.c660
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 */
}