#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/sem.h>
#include <sys/malloc.h>
#include <mach/mach_types.h>
#include <sys/filedesc.h>
#include <sys/file.h>
#define SUBSYSTEM_LOCK_AQUIRE(p) { sysv_sem_aquiring_threads++; \
while (semlock_holder != NULL) \
(void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "sysvsem", 0); \
semlock_holder = p; \
sysv_sem_aquiring_threads--; }
#define SUBSYSTEM_LOCK_RELEASE { semlock_holder = NULL; wakeup((caddr_t)&semlock_holder); }
#define UNLOCK_AND_RETURN(ret) { SUBSYSTEM_LOCK_RELEASE; return(ret); }
#define M_SYSVSEM M_SUBPROC
#if 0
static void seminit __P((void *));
SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL)
#endif 0
static struct seminfo limitseminfo = {
SEMMAP,
SEMMNI,
SEMMNS,
SEMMNU,
SEMMSL,
SEMOPM,
SEMUME,
SEMUSZ,
SEMVMX,
SEMAEM
};
struct seminfo seminfo = {
SEMMAP,
0,
0,
0,
SEMMSL,
SEMOPM,
SEMUME,
SEMUSZ,
SEMVMX,
SEMAEM
};
static long sysv_sem_sleeping_threads = 0;
static long sysv_sem_aquiring_threads = 0;
struct semctl_args;
int semctl __P((struct proc *p, struct semctl_args *uap, int *));
struct semget_args;
int semget __P((struct proc *p, struct semget_args *uap, int *));
struct semop_args;
int semop __P((struct proc *p, struct semop_args *uap, int *));
struct semconfig_args;
int semconfig __P((struct proc *p, struct semconfig_args *uap, int *));
static struct sem_undo *semu_alloc __P((struct proc *p));
static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr,
int semid, int semnum, int adjval));
static void semundo_clear __P((int semid, int semnum));
typedef int sy_call_t __P((struct proc *, void *, int *));
static sy_call_t *semcalls[] = {
(sy_call_t *)semctl, (sy_call_t *)semget,
(sy_call_t *)semop, (sy_call_t *)semconfig
};
static int semtot = 0;
struct semid_ds *sema = NULL;
struct sem *sem = NULL;
static struct sem_undo *semu_list = NULL;
struct sem_undo *semu = NULL;
static struct proc *semlock_holder = NULL;
void
seminit()
{
}
struct semsys_args {
u_int which;
int a2;
int a3;
int a4;
int a5;
};
int
semsys(p, uap, retval)
struct proc *p;
struct semsys_args *uap;
register_t *retval;
{
if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
return (EINVAL);
return ((*semcalls[uap->which])(p, &uap->a2, retval));
}
#ifndef _SYS_SYSPROTO_H_
struct semconfig_args {
semconfig_ctl_t flag;
};
#endif
int
semconfig(p, uap, retval)
struct proc *p;
struct semconfig_args *uap;
register_t *retval;
{
int eval = 0;
switch (uap->flag) {
case SEM_CONFIG_FREEZE:
SUBSYSTEM_LOCK_AQUIRE(p);
break;
case SEM_CONFIG_THAW:
SUBSYSTEM_LOCK_RELEASE;
break;
default:
printf("semconfig: unknown flag parameter value (%d) - ignored\n",
uap->flag);
eval = EINVAL;
break;
}
*retval = 0;
return(eval);
}
static int
grow_semu_array(newSize)
int newSize;
{
register int i, j;
register struct sem_undo *newSemu;
if (newSize <= seminfo.semmnu)
return 0;
if (newSize > limitseminfo.semmnu)
{
#ifdef SEM_DEBUG
printf("undo structure hard limit of %d reached, requested %d\n",
limitseminfo.semmnu, newSize);
#endif
return 0;
}
newSize = (newSize/SEMMNU_INC + 1) * SEMMNU_INC;
newSize = newSize > limitseminfo.semmnu ? limitseminfo.semmnu : newSize;
#ifdef SEM_DEBUG
printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize);
#endif
MALLOC(newSemu, struct sem_undo*, sizeof(struct sem_undo)*newSize,
M_SYSVSEM, M_WAITOK);
if (NULL == newSemu)
{
#ifdef SEM_DEBUG
printf("allocation failed. no changes made.\n");
#endif
return 0;
}
for (i = 0; i < seminfo.semmnu; i++)
{
newSemu[i] = semu[i];
for(j = 0; j < SEMUME; j++)
newSemu[i].un_ent[j] = semu[i].un_ent[j];
}
for (i = seminfo.semmnu; i < newSize; i++)
{
newSemu[i].un_proc = NULL;
}
if (semu)
FREE(semu, M_SYSVSEM);
semu = newSemu;
seminfo.semmnu = newSize;
#ifdef SEM_DEBUG
printf("expansion successful\n");
#endif
return 1;
}
static int
grow_sema_array(newSize)
int newSize;
{
register struct semid_ds *newSema;
register int i;
if (newSize <= seminfo.semmni)
return 0;
if (newSize > limitseminfo.semmni)
{
#ifdef SEM_DEBUG
printf("identifier hard limit of %d reached, requested %d\n",
limitseminfo.semmni, newSize);
#endif
return 0;
}
newSize = (newSize/SEMMNI_INC + 1) * SEMMNI_INC;
newSize = newSize > limitseminfo.semmni ? limitseminfo.semmni : newSize;
#ifdef SEM_DEBUG
printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize);
#endif
MALLOC(newSema, struct semid_ds*, sizeof(struct semid_ds)*newSize,
M_SYSVSEM, M_WAITOK);
if (NULL == newSema)
{
#ifdef SEM_DEBUG
printf("allocation failed. no changes made.\n");
#endif
return 0;
}
for (i = 0; i < seminfo.semmni; i++)
{
newSema[i] = sema[i];
if (sema[i].sem_perm.mode & SEM_ALLOC)
wakeup((caddr_t)&sema[i]);
}
for (i = seminfo.semmni; i < newSize; i++)
{
newSema[i].sem_base = 0;
newSema[i].sem_perm.mode = 0;
}
if (sema)
FREE(sema, M_SYSVSEM);
sema = newSema;
seminfo.semmni = newSize;
#ifdef SEM_DEBUG
printf("expansion successful\n");
#endif
return 1;
}
static int
grow_sem_array(newSize)
int newSize;
{
register struct sem *newSem = NULL;
register int i;
if (newSize < semtot)
return 0;
if (newSize > limitseminfo.semmns)
{
#ifdef SEM_DEBUG
printf("semaphore hard limit of %d reached, requested %d\n",
limitseminfo.semmns, newSize);
#endif
return 0;
}
newSize = (newSize/SEMMNS_INC + 1) * SEMMNS_INC;
newSize = newSize > limitseminfo.semmns ? limitseminfo.semmns : newSize;
#ifdef SEM_DEBUG
printf("growing sem array from %d to %d\n", seminfo.semmns, newSize);
#endif
MALLOC(newSem, struct sem*, sizeof(struct sem)*newSize,
M_SYSVSEM, M_WAITOK);
if (NULL == newSem)
{
#ifdef SEM_DEBUG
printf("allocation failed. no changes made.\n");
#endif
return 0;
}
if (sem)
for(i = 0; i < seminfo.semmns; i++)
newSem[i] = sem[i];
for(i = 0; i < seminfo.semmni; i++)
if (sema[i].sem_perm.mode & SEM_ALLOC)
{
if (newSem > sem)
sema[i].sem_base += newSem - sem;
else
sema[i].sem_base -= sem - newSem;
}
if (sem)
FREE(sem, M_SYSVSEM);
sem = newSem;
seminfo.semmns = newSize;
#ifdef SEM_DEBUG
printf("expansion complete\n");
#endif
return 1;
}
static struct sem_undo *
semu_alloc(p)
struct proc *p;
{
register int i;
register struct sem_undo *suptr;
register struct sem_undo **supptr;
int attempt;
for (attempt = 0; attempt < 2; attempt++) {
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);
}
}
if (attempt == 0) {
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 (!did_something)
if (!grow_semu_array(seminfo.semmnu + 1))
return(NULL);
} else {
panic("semu_alloc - second attempt failed");
}
}
return (NULL);
}
static int
semundo_adjust(p, supptr, semid, semnum, adjval)
register struct proc *p;
struct sem_undo **supptr;
int semid, semnum;
int adjval;
{
register struct sem_undo *suptr;
register struct undo *sunptr;
int i;
suptr = *supptr;
if (suptr == NULL) {
for (suptr = semu_list; suptr != NULL;
suptr = suptr->un_next) {
if (suptr->un_proc == p) {
*supptr = suptr;
break;
}
}
if (suptr == NULL) {
if (adjval == 0)
return(0);
suptr = semu_alloc(p);
if (suptr == NULL)
return(ENOSPC);
*supptr = suptr;
}
}
sunptr = &suptr->un_ent[0];
for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
if (sunptr->un_id != semid || sunptr->un_num != semnum)
continue;
if (adjval == 0)
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 (adjval == 0)
return(0);
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);
}
static void
semundo_clear(semid, semnum)
int semid, semnum;
{
register struct sem_undo *suptr;
for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
register struct undo *sunptr = &suptr->un_ent[0];
register int i = 0;
while (i < suptr->un_cnt) {
if (sunptr->un_id == semid) {
if (semnum == -1 || sunptr->un_num == semnum) {
suptr->un_cnt--;
if (i < suptr->un_cnt) {
suptr->un_ent[i] =
suptr->un_ent[suptr->un_cnt];
continue;
}
}
if (semnum != -1)
break;
}
i++, sunptr++;
}
}
}
#ifndef _SYS_SYSPROTO_H_
struct semctl_args {
int semid;
int semnum;
int cmd;
union semun arg;
};
#endif
int
semctl(p, uap, retval)
struct proc *p;
register struct semctl_args *uap;
register_t *retval;
{
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;
int i, rval, eval;
struct semid_ds sbuf;
register struct semid_ds *semaptr;
SUBSYSTEM_LOCK_AQUIRE(p);
#ifdef SEM_DEBUG
printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
#endif
semid = IPCID_TO_IX(semid);
if (semid < 0 || semid >= seminfo.semmsl)
{
#ifdef SEM_DEBUG
printf("Invalid semid\n");
#endif
UNLOCK_AND_RETURN(EINVAL);
}
semaptr = &sema[semid];
if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
UNLOCK_AND_RETURN(EINVAL);
eval = 0;
rval = 0;
switch (cmd) {
case IPC_RMID:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
UNLOCK_AND_RETURN(eval);
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;
semundo_clear(semid, -1);
wakeup((caddr_t)semaptr);
break;
case IPC_SET:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
UNLOCK_AND_RETURN(eval);
if ((eval = copyin(arg.buf, (caddr_t)&sbuf,
sizeof(sbuf))) != 0)
UNLOCK_AND_RETURN(eval);
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);
semaptr->sem_ctime = time_second;
break;
case IPC_STAT:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
eval = copyout((caddr_t)semaptr, arg.buf,
sizeof(struct semid_ds));
break;
case GETNCNT:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
UNLOCK_AND_RETURN(EINVAL);
rval = semaptr->sem_base[semnum].semncnt;
break;
case GETPID:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
UNLOCK_AND_RETURN(EINVAL);
rval = semaptr->sem_base[semnum].sempid;
break;
case GETVAL:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
UNLOCK_AND_RETURN(EINVAL);
rval = semaptr->sem_base[semnum].semval;
break;
case GETALL:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
for (i = 0; i < semaptr->sem_nsems; i++) {
eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
&arg.array[i], sizeof(arg.array[0]));
if (eval != 0)
break;
}
break;
case GETZCNT:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
UNLOCK_AND_RETURN(eval);
if (semnum < 0 || semnum >= semaptr->sem_nsems)
UNLOCK_AND_RETURN(EINVAL);
rval = semaptr->sem_base[semnum].semzcnt;
break;
case SETVAL:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
{
#ifdef SEM_DEBUG
printf("Invalid credentials for write\n");
#endif
UNLOCK_AND_RETURN(eval);
}
if (semnum < 0 || semnum >= semaptr->sem_nsems)
{
#ifdef SEM_DEBUG
printf("Invalid number out of range for set\n");
#endif
UNLOCK_AND_RETURN(EINVAL);
}
semaptr->sem_base[semnum].semval = arg.val;
semundo_clear(semid, semnum);
wakeup((caddr_t)semaptr);
break;
case SETALL:
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
UNLOCK_AND_RETURN(eval);
for (i = 0; i < semaptr->sem_nsems; i++) {
eval = copyin(&arg.array[i],
(caddr_t)&semaptr->sem_base[i].semval,
sizeof(arg.array[0]));
if (eval != 0)
break;
}
semundo_clear(semid, -1);
wakeup((caddr_t)semaptr);
break;
default:
UNLOCK_AND_RETURN(EINVAL);
}
if (eval == 0)
*retval = rval;
UNLOCK_AND_RETURN(eval);
}
#ifndef _SYS_SYSPROTO_H_
struct semget_args {
key_t key;
int nsems;
int semflg;
};
#endif
int
semget(p, uap, retval)
struct proc *p;
register struct semget_args *uap;
register_t *retval;
{
int semid, eval;
int key = uap->key;
int nsems = uap->nsems;
int semflg = uap->semflg;
struct ucred *cred = p->p_ucred;
SUBSYSTEM_LOCK_AQUIRE(p);
#ifdef SEM_DEBUG
if (key != IPC_PRIVATE)
printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
else
printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems, semflg);
#endif
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 (semid < seminfo.semmni) {
#ifdef SEM_DEBUG
printf("found public key\n");
#endif
if ((eval = ipcperm(cred, &sema[semid].sem_perm,
semflg & 0700)))
UNLOCK_AND_RETURN(eval);
if (nsems > 0 && sema[semid].sem_nsems < nsems) {
#ifdef SEM_DEBUG
printf("too small\n");
#endif
UNLOCK_AND_RETURN(EINVAL);
}
if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
#ifdef SEM_DEBUG
printf("not exclusive\n");
#endif
UNLOCK_AND_RETURN(EEXIST);
}
goto found;
}
}
#ifdef SEM_DEBUG
printf("need to allocate an id for the request\n");
#endif
if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
if (nsems <= 0 || nsems > seminfo.semmsl) {
#ifdef SEM_DEBUG
printf("nsems out of range (0<%d<=%d)\n", nsems,
seminfo.semmsl);
#endif
UNLOCK_AND_RETURN(EINVAL);
}
if (nsems > seminfo.semmns - semtot) {
#ifdef SEM_DEBUG
printf("not enough semaphores left (need %d, got %d)\n",
nsems, seminfo.semmns - semtot);
#endif
if (!grow_sem_array(semtot + nsems))
{
#ifdef SEM_DEBUG
printf("failed to grow the sem array\n");
#endif
UNLOCK_AND_RETURN(ENOSPC);
}
}
for (semid = 0; semid < seminfo.semmni; semid++) {
if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
break;
}
if (semid == seminfo.semmni) {
#ifdef SEM_DEBUG
printf("no more id's available\n");
#endif
if (!grow_sema_array(seminfo.semmni + 1))
{
#ifdef SEM_DEBUG
printf("failed to grow sema array\n");
#endif
UNLOCK_AND_RETURN(ENOSPC);
}
}
#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_second;
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 = 0x%x, next = 0x%x\n", sema[semid].sem_base,
&sem[semtot]);
#endif
} else {
#ifdef SEM_DEBUG
printf("didn't find it and wasn't asked to create it\n");
#endif
UNLOCK_AND_RETURN(ENOENT);
}
found:
*retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
#ifdef SEM_DEBUG
printf("semget is done, returning %d\n", *retval);
#endif
SUBSYSTEM_LOCK_RELEASE;
return(0);
}
#ifndef _SYS_SYSPROTO_H_
struct semop_args {
int semid;
struct sembuf *sops;
int nsops;
};
#endif
int
semop(p, uap, retval)
struct proc *p;
register struct semop_args *uap;
register_t *retval;
{
int semid = uap->semid;
int nsops = uap->nsops;
struct sembuf sops[MAX_SOPS];
register struct semid_ds *semaptr;
register struct sembuf *sopptr;
register struct sem *semptr;
struct sem_undo *suptr = NULL;
struct ucred *cred = p->p_ucred;
int i, j, eval;
int do_wakeup, do_undos;
SUBSYSTEM_LOCK_AQUIRE(p);
#ifdef SEM_DEBUG
printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
#endif
semid = IPCID_TO_IX(semid);
if (semid < 0 || semid >= seminfo.semmsl)
UNLOCK_AND_RETURN(EINVAL);
semaptr = &sema[semid];
if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
UNLOCK_AND_RETURN(EINVAL);
if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
UNLOCK_AND_RETURN(EINVAL);
if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
#ifdef SEM_DEBUG
printf("eval = %d from ipaccess\n", eval);
#endif
UNLOCK_AND_RETURN(eval);
}
if (nsops > MAX_SOPS) {
#ifdef SEM_DEBUG
printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
#endif
UNLOCK_AND_RETURN(E2BIG);
}
if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
#ifdef SEM_DEBUG
printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
uap->sops, &sops, nsops * sizeof(sops[0]));
#endif
UNLOCK_AND_RETURN(eval);
}
do_undos = 0;
for (;;) {
do_wakeup = 0;
for (i = 0; i < nsops; i++) {
sopptr = &sops[i];
if (sopptr->sem_num >= semaptr->sem_nsems)
UNLOCK_AND_RETURN(EFBIG);
semptr = &semaptr->sem_base[sopptr->sem_num];
#ifdef SEM_DEBUG
printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
semaptr, semaptr->sem_base, semptr,
sopptr->sem_num, semptr->semval, sopptr->sem_op,
(sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
#endif
if (sopptr->sem_op < 0) {
if (semptr->semval + sopptr->sem_op < 0) {
#ifdef SEM_DEBUG
printf("semop: can't do it now\n");
#endif
break;
} else {
semptr->semval += sopptr->sem_op;
if (semptr->semval == 0 &&
semptr->semzcnt > 0)
do_wakeup = 1;
}
if (sopptr->sem_flg & SEM_UNDO)
do_undos = 1;
} else if (sopptr->sem_op == 0) {
if (semptr->semval > 0) {
#ifdef SEM_DEBUG
printf("semop: not zero now\n");
#endif
break;
}
} else {
if (semptr->semncnt > 0)
do_wakeup = 1;
semptr->semval += sopptr->sem_op;
if (sopptr->sem_flg & SEM_UNDO)
do_undos = 1;
}
}
if (i >= nsops)
goto done;
#ifdef SEM_DEBUG
printf("semop: rollback 0 through %d\n", i-1);
#endif
for (j = 0; j < i; j++)
semaptr->sem_base[sops[j].sem_num].semval -=
sops[j].sem_op;
if (sopptr->sem_flg & IPC_NOWAIT)
UNLOCK_AND_RETURN(EAGAIN);
if (sopptr->sem_op == 0)
semptr->semzcnt++;
else
semptr->semncnt++;
#ifdef SEM_DEBUG
printf("semop: good night!\n");
#endif
SUBSYSTEM_LOCK_RELEASE;
sysv_sem_sleeping_threads++;
eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
"semwait", 0);
sysv_sem_sleeping_threads--;
#ifdef SEM_DEBUG
printf("semop: good morning (eval=%d)!\n", eval);
#endif
if (eval != 0)
return(EINTR);
SUBSYSTEM_LOCK_AQUIRE(p);
suptr = NULL;
semaptr = &sema[semid];
#ifdef SEM_DEBUG
printf("semop: good morning!\n");
#endif
if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
#ifdef EIDRM
UNLOCK_AND_RETURN(EIDRM);
#else
UNLOCK_AND_RETURN(EINVAL);
#endif
}
semptr = &semaptr->sem_base[sopptr->sem_num];
if (sopptr->sem_op == 0)
semptr->semzcnt--;
else
semptr->semncnt--;
}
done:
if (do_undos) {
for (i = 0; i < nsops; i++) {
int adjval;
if ((sops[i].sem_flg & SEM_UNDO) == 0)
continue;
adjval = sops[i].sem_op;
if (adjval == 0)
continue;
eval = semundo_adjust(p, &suptr, semid,
sops[i].sem_num, -adjval);
if (eval == 0)
continue;
for (j = i - 1; j >= 0; j--) {
if ((sops[j].sem_flg & SEM_UNDO) == 0)
continue;
adjval = sops[j].sem_op;
if (adjval == 0)
continue;
if (semundo_adjust(p, &suptr, semid,
sops[j].sem_num, adjval) != 0)
panic("semop - can't undo undos");
}
for (j = 0; j < nsops; j++)
semaptr->sem_base[sops[j].sem_num].semval -=
sops[j].sem_op;
#ifdef SEM_DEBUG
printf("eval = %d from semundo_adjust\n", eval);
#endif
UNLOCK_AND_RETURN(eval);
}
}
for (i = 0; i < nsops; i++) {
sopptr = &sops[i];
semptr = &semaptr->sem_base[sopptr->sem_num];
semptr->sempid = p->p_pid;
}
SUBSYSTEM_LOCK_RELEASE;
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
printf("semop: back from wakeup\n");
#else
wakeup((caddr_t)semaptr);
#endif
}
#ifdef SEM_DEBUG
printf("semop: done\n");
#endif
*retval = 0;
return(0);
}
void
semexit(p)
struct proc *p;
{
register struct sem_undo *suptr;
register struct sem_undo **supptr;
int did_something;
SUBSYSTEM_LOCK_AQUIRE(p);
if (!sem)
{
SUBSYSTEM_LOCK_RELEASE;
return;
}
did_something = 0;
for (supptr = &semu_list; (suptr = *supptr) != NULL;
supptr = &suptr->un_next) {
if (suptr->un_proc == p)
break;
}
if (suptr == NULL)
goto unlock;
#ifdef SEM_DEBUG
printf("proc @%08x has undo structure with %d entries\n", p,
suptr->un_cnt);
#endif
if (suptr->un_cnt > 0) {
int ix;
for (ix = 0; ix < suptr->un_cnt; ix++) {
int semid = suptr->un_ent[ix].un_id;
int semnum = suptr->un_ent[ix].un_num;
int adjval = suptr->un_ent[ix].un_adjval;
struct semid_ds *semaptr;
semaptr = &sema[semid];
if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
panic("semexit - semid not allocated");
if (semnum >= semaptr->sem_nsems)
panic("semexit - semnum out of range");
#ifdef SEM_DEBUG
printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
suptr->un_proc, suptr->un_ent[ix].un_id,
suptr->un_ent[ix].un_num,
suptr->un_ent[ix].un_adjval,
semaptr->sem_base[semnum].semval);
#endif
if (adjval < 0) {
if (semaptr->sem_base[semnum].semval < -adjval)
semaptr->sem_base[semnum].semval = 0;
else
semaptr->sem_base[semnum].semval +=
adjval;
} else
semaptr->sem_base[semnum].semval += adjval;
#ifdef SEM_WAKEUP
sem_wakeup((caddr_t)semaptr);
#else
wakeup((caddr_t)semaptr);
#endif
#ifdef SEM_DEBUG
printf("semexit: back from wakeup\n");
#endif
}
}
#ifdef SEM_DEBUG
printf("removing vector\n");
#endif
suptr->un_proc = NULL;
*supptr = suptr->un_next;
unlock:
SUBSYSTEM_LOCK_RELEASE;
}