sysv_sem.c revision 111119
150477Speter/* $FreeBSD: head/sys/kern/sysv_sem.c 111119 2003-02-19 05:47:46Z imp $ */
22729Sdfr
32729Sdfr/*
42729Sdfr * Implementation of SVID semaphores
52729Sdfr *
62729Sdfr * Author:  Daniel Boulet
72729Sdfr *
82729Sdfr * This software is provided ``AS IS'' without any warranties of any kind.
92729Sdfr */
102729Sdfr
1159839Speter#include "opt_sysvipc.h"
1259839Speter
132729Sdfr#include <sys/param.h>
142729Sdfr#include <sys/systm.h>
1511626Sbde#include <sys/sysproto.h>
162729Sdfr#include <sys/kernel.h>
172729Sdfr#include <sys/proc.h>
1882607Sdillon#include <sys/lock.h>
1982607Sdillon#include <sys/mutex.h>
202729Sdfr#include <sys/sem.h>
2169449Salfred#include <sys/syscall.h>
2211626Sbde#include <sys/sysent.h>
2359839Speter#include <sys/sysctl.h>
2459839Speter#include <sys/malloc.h>
2568024Srwatson#include <sys/jail.h>
262729Sdfr
2759839Speterstatic MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores");
2859839Speter
29100523Salfred#ifdef SEM_DEBUG
30100523Salfred#define DPRINTF(a)	printf a
31100523Salfred#else
32100523Salfred#define DPRINTF(a)
33100523Salfred#endif
34100523Salfred
3592723Salfredstatic void seminit(void);
3692723Salfredstatic int sysvsem_modload(struct module *, int, void *);
3792723Salfredstatic int semunload(void);
3892723Salfredstatic void semexit_myhook(struct proc *p);
3992723Salfredstatic int sysctl_sema(SYSCTL_HANDLER_ARGS);
40101774Salfredstatic int semvalid(int semid, struct semid_ds *semaptr);
4110358Sjulian
4212866Speter#ifndef _SYS_SYSPROTO_H_
4312866Speterstruct __semctl_args;
4492723Salfredint __semctl(struct thread *td, struct __semctl_args *uap);
4511626Sbdestruct semget_args;
4692723Salfredint semget(struct thread *td, struct semget_args *uap);
4711626Sbdestruct semop_args;
4892723Salfredint semop(struct thread *td, struct semop_args *uap);
4912866Speter#endif
5011626Sbde
5192723Salfredstatic struct sem_undo *semu_alloc(struct thread *td);
5292723Salfredstatic int semundo_adjust(struct thread *td, struct sem_undo **supptr,
5392723Salfred		int semid, int semnum, int adjval);
5492723Salfredstatic void semundo_clear(int semid, int semnum);
5511626Sbde
5611626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */
5712819Sphkstatic sy_call_t *semcalls[] = {
5812866Speter	(sy_call_t *)__semctl, (sy_call_t *)semget,
5959828Speter	(sy_call_t *)semop
6011626Sbde};
6111626Sbde
62101774Salfredstatic struct mtx	sem_mtx;	/* semaphore global lock */
6312819Sphkstatic int	semtot = 0;
6459839Speterstatic struct semid_ds *sema;	/* semaphore id pool */
65101774Salfredstatic struct mtx *sema_mtx;	/* semaphore id pool mutexes*/
6659839Speterstatic struct sem *sem;		/* semaphore pool */
67101774SalfredSLIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
6859839Speterstatic int	*semu;		/* undo structure pool */
692729Sdfr
70101774Salfred#define SEMUNDO_MTX		sem_mtx
71101774Salfred#define SEMUNDO_LOCK()		mtx_lock(&SEMUNDO_MTX);
72101774Salfred#define SEMUNDO_UNLOCK()	mtx_unlock(&SEMUNDO_MTX);
73101774Salfred#define SEMUNDO_LOCKASSERT(how)	mtx_assert(&SEMUNDO_MTX, (how));
74101774Salfred
7559839Speterstruct sem {
7659839Speter	u_short	semval;		/* semaphore value */
7759839Speter	pid_t	sempid;		/* pid of last operation */
7859839Speter	u_short	semncnt;	/* # awaiting semval > cval */
7959839Speter	u_short	semzcnt;	/* # awaiting semval = 0 */
8059839Speter};
8159839Speter
8259839Speter/*
8359839Speter * Undo structure (one per process)
8459839Speter */
8559839Speterstruct sem_undo {
86101774Salfred	SLIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
8759839Speter	struct	proc *un_proc;		/* owner of this structure */
8859839Speter	short	un_cnt;			/* # of active entries */
8959839Speter	struct undo {
9059839Speter		short	un_adjval;	/* adjust on exit values */
9159839Speter		short	un_num;		/* semaphore # */
9259839Speter		int	un_id;		/* semid */
9359839Speter	} un_ent[1];			/* undo entries */
9459839Speter};
9559839Speter
9659839Speter/*
9759839Speter * Configuration parameters
9859839Speter */
9959839Speter#ifndef SEMMNI
10059839Speter#define SEMMNI	10		/* # of semaphore identifiers */
10159839Speter#endif
10259839Speter#ifndef SEMMNS
10359839Speter#define SEMMNS	60		/* # of semaphores in system */
10459839Speter#endif
10559839Speter#ifndef SEMUME
10659839Speter#define SEMUME	10		/* max # of undo entries per process */
10759839Speter#endif
10859839Speter#ifndef SEMMNU
10959839Speter#define SEMMNU	30		/* # of undo structures in system */
11059839Speter#endif
11159839Speter
11259839Speter/* shouldn't need tuning */
11359839Speter#ifndef SEMMAP
11459839Speter#define SEMMAP	30		/* # of entries in semaphore map */
11559839Speter#endif
11659839Speter#ifndef SEMMSL
11759839Speter#define SEMMSL	SEMMNS		/* max # of semaphores per id */
11859839Speter#endif
11959839Speter#ifndef SEMOPM
12059839Speter#define SEMOPM	100		/* max # of operations per semop call */
12159839Speter#endif
12259839Speter
12359839Speter#define SEMVMX	32767		/* semaphore maximum value */
12459839Speter#define SEMAEM	16384		/* adjust on exit max value */
12559839Speter
12659839Speter/*
12759839Speter * Due to the way semaphore memory is allocated, we have to ensure that
12859839Speter * SEMUSZ is properly aligned.
12959839Speter */
13059839Speter
13159839Speter#define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
13259839Speter
13359839Speter/* actual size of an undo structure */
13459839Speter#define SEMUSZ	SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
13559839Speter
13659839Speter/*
13759839Speter * Macro to find a particular sem_undo vector
13859839Speter */
139101350Salfred#define SEMU(ix) \
140101350Salfred	((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
14159839Speter
14259839Speter/*
14359839Speter * semaphore info struct
14459839Speter */
14559839Speterstruct seminfo seminfo = {
14659839Speter                SEMMAP,         /* # of entries in semaphore map */
14759839Speter                SEMMNI,         /* # of semaphore identifiers */
14859839Speter                SEMMNS,         /* # of semaphores in system */
14959839Speter                SEMMNU,         /* # of undo structures in system */
15059839Speter                SEMMSL,         /* max # of semaphores per id */
15159839Speter                SEMOPM,         /* max # of operations per semop call */
15259839Speter                SEMUME,         /* max # of undo entries per process */
15359839Speter                SEMUSZ,         /* size in bytes of undo structure */
15459839Speter                SEMVMX,         /* semaphore maximum value */
15559839Speter                SEMAEM          /* adjust on exit max value */
15659839Speter};
15759839Speter
15859839SpeterSYSCTL_DECL(_kern_ipc);
15959839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, "");
16059839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, "");
16159839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, "");
16259839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, "");
16359839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, "");
16459839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, "");
16559839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, "");
16659839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, "");
16759839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, "");
16859839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, "");
16977461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD,
17077461Sdd    NULL, 0, sysctl_sema, "", "");
17159839Speter
17259839Speterstatic void
17369449Salfredseminit(void)
1742729Sdfr{
175101350Salfred	int i;
1762729Sdfr
17783413Smr	TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo.semmap);
17883413Smr	TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo.semmni);
17983413Smr	TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo.semmns);
18083413Smr	TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo.semmnu);
18183413Smr	TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo.semmsl);
18283413Smr	TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo.semopm);
18383413Smr	TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo.semume);
18483413Smr	TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo.semusz);
18583413Smr	TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo.semvmx);
18683413Smr	TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo.semaem);
18783413Smr
188111119Simp	sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK);
189101350Salfred	sema = malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM,
190111119Simp	    M_WAITOK);
191101774Salfred	sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM,
192111119Simp	    M_WAITOK | M_ZERO);
193111119Simp	semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK);
1942729Sdfr
1952729Sdfr	for (i = 0; i < seminfo.semmni; i++) {
1962729Sdfr		sema[i].sem_base = 0;
1972729Sdfr		sema[i].sem_perm.mode = 0;
1982729Sdfr	}
199101774Salfred	for (i = 0; i < seminfo.semmni; i++)
200101774Salfred		mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF);
2012729Sdfr	for (i = 0; i < seminfo.semmnu; i++) {
202101350Salfred		struct sem_undo *suptr = SEMU(i);
2032729Sdfr		suptr->un_proc = NULL;
2042729Sdfr	}
205101774Salfred	SLIST_INIT(&semu_list);
20688715Salc	at_exit(semexit_myhook);
207101774Salfred	mtx_init(&sem_mtx, "sem", NULL, MTX_DEF);
2082729Sdfr}
2092729Sdfr
21069449Salfredstatic int
21169449Salfredsemunload(void)
21269449Salfred{
213101774Salfred	int i;
21469449Salfred
21569449Salfred	if (semtot != 0)
21669449Salfred		return (EBUSY);
21769449Salfred
21869449Salfred	free(sem, M_SEM);
21969449Salfred	free(sema, M_SEM);
22069449Salfred	free(semu, M_SEM);
22188715Salc	rm_at_exit(semexit_myhook);
222101774Salfred	for (i = 0; i < seminfo.semmni; i++)
223101774Salfred		mtx_destroy(&sema_mtx[i]);
224101774Salfred	mtx_destroy(&sem_mtx);
22569449Salfred	return (0);
22669449Salfred}
22769449Salfred
22869449Salfredstatic int
22969449Salfredsysvsem_modload(struct module *module, int cmd, void *arg)
23069449Salfred{
23169449Salfred	int error = 0;
23269449Salfred
23369449Salfred	switch (cmd) {
23469449Salfred	case MOD_LOAD:
23569449Salfred		seminit();
23669449Salfred		break;
23769449Salfred	case MOD_UNLOAD:
23869449Salfred		error = semunload();
23969449Salfred		break;
24069449Salfred	case MOD_SHUTDOWN:
24169449Salfred		break;
24269449Salfred	default:
24369449Salfred		error = EINVAL;
24469449Salfred		break;
24569449Salfred	}
24669449Salfred	return (error);
24769449Salfred}
24869449Salfred
24971038Sdesstatic moduledata_t sysvsem_mod = {
25071038Sdes	"sysvsem",
25169449Salfred	&sysvsem_modload,
25269449Salfred	NULL
25369449Salfred};
25469449Salfred
25588633SalfredSYSCALL_MODULE_HELPER(semsys);
25688633SalfredSYSCALL_MODULE_HELPER(__semctl);
25788633SalfredSYSCALL_MODULE_HELPER(semget);
25888633SalfredSYSCALL_MODULE_HELPER(semop);
25969449Salfred
26071038SdesDECLARE_MODULE(sysvsem, sysvsem_mod,
26169449Salfred	SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
26271038SdesMODULE_VERSION(sysvsem, 1);
26369449Salfred
2642729Sdfr/*
2652729Sdfr * Entry point for all SEM calls
26682607Sdillon *
26782607Sdillon * MPSAFE
2682729Sdfr */
2692729Sdfrint
27083366Sjuliansemsys(td, uap)
27183366Sjulian	struct thread *td;
27211626Sbde	/* XXX actually varargs. */
27311626Sbde	struct semsys_args /* {
27411626Sbde		u_int	which;
27511626Sbde		int	a2;
27611626Sbde		int	a3;
27711626Sbde		int	a4;
27811626Sbde		int	a5;
27911626Sbde	} */ *uap;
2802729Sdfr{
28182607Sdillon	int error;
2822729Sdfr
28391703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
28491703Sjhb		return (ENOSYS);
28591703Sjhb	if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
28691703Sjhb		return (EINVAL);
28783366Sjulian	error = (*semcalls[uap->which])(td, &uap->a2);
28882607Sdillon	return (error);
2892729Sdfr}
2902729Sdfr
2912729Sdfr/*
2922729Sdfr * Allocate a new sem_undo structure for a process
2932729Sdfr * (returns ptr to structure or NULL if no more room)
2942729Sdfr */
2952729Sdfr
29612819Sphkstatic struct sem_undo *
29783366Sjuliansemu_alloc(td)
29883366Sjulian	struct thread *td;
2992729Sdfr{
300101350Salfred	int i;
301101350Salfred	struct sem_undo *suptr;
302101350Salfred	struct sem_undo **supptr;
3032729Sdfr	int attempt;
3042729Sdfr
305101774Salfred	SEMUNDO_LOCKASSERT(MA_OWNED);
3062729Sdfr	/*
3072729Sdfr	 * Try twice to allocate something.
3082729Sdfr	 * (we'll purge any empty structures after the first pass so
3092729Sdfr	 * two passes are always enough)
3102729Sdfr	 */
3112729Sdfr
3122729Sdfr	for (attempt = 0; attempt < 2; attempt++) {
3132729Sdfr		/*
3142729Sdfr		 * Look for a free structure.
3152729Sdfr		 * Fill it in and return it if we find one.
3162729Sdfr		 */
3172729Sdfr
3182729Sdfr		for (i = 0; i < seminfo.semmnu; i++) {
3192729Sdfr			suptr = SEMU(i);
3202729Sdfr			if (suptr->un_proc == NULL) {
321101774Salfred				SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
3222729Sdfr				suptr->un_cnt = 0;
32383366Sjulian				suptr->un_proc = td->td_proc;
3242729Sdfr				return(suptr);
3252729Sdfr			}
3262729Sdfr		}
3272729Sdfr
3282729Sdfr		/*
3292729Sdfr		 * We didn't find a free one, if this is the first attempt
3302729Sdfr		 * then try to free some structures.
3312729Sdfr		 */
3322729Sdfr
3332729Sdfr		if (attempt == 0) {
3342729Sdfr			/* All the structures are in use - try to free some */
3352729Sdfr			int did_something = 0;
3362729Sdfr
337101774Salfred			SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list,
338101774Salfred			    un_next) {
339101774Salfred				if (suptr->un_cnt == 0) {
3402729Sdfr					suptr->un_proc = NULL;
3412729Sdfr					did_something = 1;
342101774Salfred					*supptr = SLIST_NEXT(suptr, un_next);
343101774Salfred				}
3442729Sdfr			}
3452729Sdfr
3462729Sdfr			/* If we didn't free anything then just give-up */
3472729Sdfr			if (!did_something)
3482729Sdfr				return(NULL);
3492729Sdfr		} else {
3502729Sdfr			/*
3512729Sdfr			 * The second pass failed even though we freed
3522729Sdfr			 * something after the first pass!
3532729Sdfr			 * This is IMPOSSIBLE!
3542729Sdfr			 */
3552729Sdfr			panic("semu_alloc - second attempt failed");
3562729Sdfr		}
3572729Sdfr	}
3582836Sdg	return (NULL);
3592729Sdfr}
3602729Sdfr
3612729Sdfr/*
3622729Sdfr * Adjust a particular entry for a particular proc
3632729Sdfr */
3642729Sdfr
36512819Sphkstatic int
36683366Sjuliansemundo_adjust(td, supptr, semid, semnum, adjval)
367101350Salfred	struct thread *td;
3682729Sdfr	struct sem_undo **supptr;
3692729Sdfr	int semid, semnum;
3702729Sdfr	int adjval;
3712729Sdfr{
37283366Sjulian	struct proc *p = td->td_proc;
373101350Salfred	struct sem_undo *suptr;
374101350Salfred	struct undo *sunptr;
3752729Sdfr	int i;
3762729Sdfr
377101774Salfred	SEMUNDO_LOCKASSERT(MA_OWNED);
3782729Sdfr	/* Look for and remember the sem_undo if the caller doesn't provide
3792729Sdfr	   it */
3802729Sdfr
3812729Sdfr	suptr = *supptr;
3822729Sdfr	if (suptr == NULL) {
383101774Salfred		SLIST_FOREACH(suptr, &semu_list, un_next) {
3842729Sdfr			if (suptr->un_proc == p) {
3852729Sdfr				*supptr = suptr;
3862729Sdfr				break;
3872729Sdfr			}
3882729Sdfr		}
3892729Sdfr		if (suptr == NULL) {
3902729Sdfr			if (adjval == 0)
3912729Sdfr				return(0);
39283366Sjulian			suptr = semu_alloc(td);
3932729Sdfr			if (suptr == NULL)
3942729Sdfr				return(ENOSPC);
3952729Sdfr			*supptr = suptr;
3962729Sdfr		}
3972729Sdfr	}
3982729Sdfr
3992729Sdfr	/*
4002729Sdfr	 * Look for the requested entry and adjust it (delete if adjval becomes
4012729Sdfr	 * 0).
4022729Sdfr	 */
4032729Sdfr	sunptr = &suptr->un_ent[0];
4042729Sdfr	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
4052729Sdfr		if (sunptr->un_id != semid || sunptr->un_num != semnum)
4062729Sdfr			continue;
40784789Smr		if (adjval != 0) {
40884789Smr			adjval += sunptr->un_adjval;
40984789Smr			if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
41084789Smr				return (ERANGE);
41184789Smr		}
41284789Smr		sunptr->un_adjval = adjval;
4132729Sdfr		if (sunptr->un_adjval == 0) {
4142729Sdfr			suptr->un_cnt--;
4152729Sdfr			if (i < suptr->un_cnt)
4162729Sdfr				suptr->un_ent[i] =
4172729Sdfr				    suptr->un_ent[suptr->un_cnt];
4182729Sdfr		}
4192729Sdfr		return(0);
4202729Sdfr	}
4212729Sdfr
4222729Sdfr	/* Didn't find the right entry - create it */
4232729Sdfr	if (adjval == 0)
4242729Sdfr		return(0);
42584789Smr	if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
42684789Smr		return (ERANGE);
42741774Sdillon	if (suptr->un_cnt != seminfo.semume) {
4282729Sdfr		sunptr = &suptr->un_ent[suptr->un_cnt];
4292729Sdfr		suptr->un_cnt++;
4302729Sdfr		sunptr->un_adjval = adjval;
4312729Sdfr		sunptr->un_id = semid; sunptr->un_num = semnum;
4322729Sdfr	} else
4332729Sdfr		return(EINVAL);
4342729Sdfr	return(0);
4352729Sdfr}
4362729Sdfr
43712819Sphkstatic void
4382729Sdfrsemundo_clear(semid, semnum)
4392729Sdfr	int semid, semnum;
4402729Sdfr{
441101350Salfred	struct sem_undo *suptr;
4422729Sdfr
443101774Salfred	SEMUNDO_LOCKASSERT(MA_OWNED);
444101774Salfred	SLIST_FOREACH(suptr, &semu_list, un_next) {
445101350Salfred		struct undo *sunptr = &suptr->un_ent[0];
446101350Salfred		int i = 0;
4472729Sdfr
4482729Sdfr		while (i < suptr->un_cnt) {
4492729Sdfr			if (sunptr->un_id == semid) {
4502729Sdfr				if (semnum == -1 || sunptr->un_num == semnum) {
4512729Sdfr					suptr->un_cnt--;
4522729Sdfr					if (i < suptr->un_cnt) {
4532729Sdfr						suptr->un_ent[i] =
4542729Sdfr						  suptr->un_ent[suptr->un_cnt];
4552729Sdfr						continue;
4562729Sdfr					}
4572729Sdfr				}
4582729Sdfr				if (semnum != -1)
4592729Sdfr					break;
4602729Sdfr			}
4612729Sdfr			i++, sunptr++;
4622729Sdfr		}
4632729Sdfr	}
4642729Sdfr}
4652729Sdfr
466101774Salfredstatic int
467101774Salfredsemvalid(semid, semaptr)
468101774Salfred	int semid;
469101774Salfred	struct semid_ds *semaptr;
470101774Salfred{
471101774Salfred
472101774Salfred	return ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
473101774Salfred	    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0);
474101774Salfred}
475101774Salfred
47612866Speter/*
47712866Speter * Note that the user-mode half of this passes a union, not a pointer
47812866Speter */
47912866Speter#ifndef _SYS_SYSPROTO_H_
48012866Speterstruct __semctl_args {
4812729Sdfr	int	semid;
4822729Sdfr	int	semnum;
4832729Sdfr	int	cmd;
4842729Sdfr	union	semun *arg;
4852729Sdfr};
48612866Speter#endif
4872729Sdfr
48882607Sdillon/*
48982607Sdillon * MPSAFE
49082607Sdillon */
49112866Speterint
49283366Sjulian__semctl(td, uap)
49383366Sjulian	struct thread *td;
494101350Salfred	struct __semctl_args *uap;
4952729Sdfr{
4962729Sdfr	int semid = uap->semid;
4972729Sdfr	int semnum = uap->semnum;
4982729Sdfr	int cmd = uap->cmd;
499101774Salfred	u_short *array;
5002729Sdfr	union semun *arg = uap->arg;
5012729Sdfr	union semun real_arg;
50291406Sjhb	struct ucred *cred = td->td_ucred;
50382607Sdillon	int i, rval, error;
5042729Sdfr	struct semid_ds sbuf;
505101350Salfred	struct semid_ds *semaptr;
506101774Salfred	struct mtx *sema_mtxp;
507101774Salfred	u_short usval, count;
5082729Sdfr
509100523Salfred	DPRINTF(("call to semctl(%d, %d, %d, 0x%x)\n",
510100523Salfred	    semid, semnum, cmd, arg));
51191703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
51291703Sjhb		return (ENOSYS);
51391703Sjhb
514101774Salfred	array = NULL;
515101774Salfred
51683414Smr	switch(cmd) {
51783414Smr	case SEM_STAT:
51891744Smaxim		if (semid < 0 || semid >= seminfo.semmni)
519101774Salfred			return (EINVAL);
520101774Salfred		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
521101774Salfred			return (error);
52283414Smr		semaptr = &sema[semid];
523101774Salfred		sema_mtxp = &sema_mtx[semid];
524101774Salfred		mtx_lock(sema_mtxp);
525101774Salfred		if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ) {
526101774Salfred			error = EINVAL;
527101774Salfred			goto done2;
528101774Salfred		}
52983414Smr		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
530101774Salfred			goto done2;
531101774Salfred		mtx_unlock(sema_mtxp);
532100511Salfred		error = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds));
53383414Smr		rval = IXSEQ_TO_IPCID(semid,semaptr->sem_perm);
53483414Smr		if (error == 0)
53583414Smr			td->td_retval[0] = rval;
536101774Salfred		return (error);
53783414Smr	}
53883414Smr
5392729Sdfr	semid = IPCID_TO_IX(semid);
540101774Salfred	if (semid < 0 || semid >= seminfo.semmni)
541101774Salfred		return (EINVAL);
5422729Sdfr
5432729Sdfr	semaptr = &sema[semid];
544101774Salfred	sema_mtxp = &sema_mtx[semid];
545101774Salfred
54682607Sdillon	error = 0;
5472729Sdfr	rval = 0;
5482729Sdfr
5492729Sdfr	switch (cmd) {
5502729Sdfr	case IPC_RMID:
551101774Salfred		mtx_lock(sema_mtxp);
552101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
553101774Salfred			goto done2;
55483366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M)))
55582607Sdillon			goto done2;
5562729Sdfr		semaptr->sem_perm.cuid = cred->cr_uid;
5572729Sdfr		semaptr->sem_perm.uid = cred->cr_uid;
5582729Sdfr		semtot -= semaptr->sem_nsems;
5592729Sdfr		for (i = semaptr->sem_base - sem; i < semtot; i++)
5602729Sdfr			sem[i] = sem[i + semaptr->sem_nsems];
5612729Sdfr		for (i = 0; i < seminfo.semmni; i++) {
5622729Sdfr			if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
5632729Sdfr			    sema[i].sem_base > semaptr->sem_base)
5642729Sdfr				sema[i].sem_base -= semaptr->sem_nsems;
5652729Sdfr		}
5662729Sdfr		semaptr->sem_perm.mode = 0;
567101774Salfred		SEMUNDO_LOCK();
5682729Sdfr		semundo_clear(semid, -1);
569101774Salfred		SEMUNDO_UNLOCK();
570100511Salfred		wakeup(semaptr);
5712729Sdfr		break;
5722729Sdfr
5732729Sdfr	case IPC_SET:
57482607Sdillon		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
57582607Sdillon			goto done2;
576101774Salfred		if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0)
57782607Sdillon			goto done2;
578101774Salfred		mtx_lock(sema_mtxp);
579101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
580101774Salfred			goto done2;
581101774Salfred		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M)))
582101774Salfred			goto done2;
5832729Sdfr		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
5842729Sdfr		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
5852729Sdfr		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
5862729Sdfr		    (sbuf.sem_perm.mode & 0777);
58734961Sphk		semaptr->sem_ctime = time_second;
5882729Sdfr		break;
5892729Sdfr
5902729Sdfr	case IPC_STAT:
591101774Salfred		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
592101774Salfred			goto done2;
593101774Salfred		mtx_lock(sema_mtxp);
594101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
595101774Salfred			goto done2;
59683366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
59782607Sdillon			goto done2;
598101774Salfred		sbuf = *semaptr;
599101774Salfred		mtx_unlock(sema_mtxp);
600100511Salfred		error = copyout(semaptr, real_arg.buf,
60182607Sdillon				sizeof(struct semid_ds));
6022729Sdfr		break;
6032729Sdfr
6042729Sdfr	case GETNCNT:
605101774Salfred		mtx_lock(sema_mtxp);
606101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
607101774Salfred			goto done2;
60883366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
60982607Sdillon			goto done2;
61082607Sdillon		if (semnum < 0 || semnum >= semaptr->sem_nsems) {
61182607Sdillon			error = EINVAL;
61282607Sdillon			goto done2;
61382607Sdillon		}
6142729Sdfr		rval = semaptr->sem_base[semnum].semncnt;
6152729Sdfr		break;
6162729Sdfr
6172729Sdfr	case GETPID:
618101774Salfred		mtx_lock(sema_mtxp);
619101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
620101774Salfred			goto done2;
62183366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
62282607Sdillon			goto done2;
62382607Sdillon		if (semnum < 0 || semnum >= semaptr->sem_nsems) {
62482607Sdillon			error = EINVAL;
62582607Sdillon			goto done2;
62682607Sdillon		}
6272729Sdfr		rval = semaptr->sem_base[semnum].sempid;
6282729Sdfr		break;
6292729Sdfr
6302729Sdfr	case GETVAL:
631101774Salfred		mtx_lock(sema_mtxp);
632101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
633101774Salfred			goto done2;
63483366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
63582607Sdillon			goto done2;
63682607Sdillon		if (semnum < 0 || semnum >= semaptr->sem_nsems) {
63782607Sdillon			error = EINVAL;
63882607Sdillon			goto done2;
63982607Sdillon		}
6402729Sdfr		rval = semaptr->sem_base[semnum].semval;
6412729Sdfr		break;
6422729Sdfr
6432729Sdfr	case GETALL:
644101774Salfred		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
645101774Salfred			goto done2;
646101774Salfred		array = malloc(sizeof(*array) * semaptr->sem_nsems, M_TEMP,
647111119Simp		    M_WAITOK);
648101774Salfred		mtx_lock(sema_mtxp);
649101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
650101774Salfred			goto done2;
65183366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
65282607Sdillon			goto done2;
653101774Salfred		for (i = 0; i < semaptr->sem_nsems; i++)
654101774Salfred			array[i] = semaptr->sem_base[i].semval;
655101774Salfred		mtx_unlock(sema_mtxp);
656101774Salfred		error = copyout(array, real_arg.array,
657101774Salfred		    i * sizeof(real_arg.array[0]));
6582729Sdfr		break;
6592729Sdfr
6602729Sdfr	case GETZCNT:
661101774Salfred		mtx_lock(sema_mtxp);
662101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
663101774Salfred			goto done2;
66483366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
66582607Sdillon			goto done2;
66682607Sdillon		if (semnum < 0 || semnum >= semaptr->sem_nsems) {
66782607Sdillon			error = EINVAL;
66882607Sdillon			goto done2;
66982607Sdillon		}
6702729Sdfr		rval = semaptr->sem_base[semnum].semzcnt;
6712729Sdfr		break;
6722729Sdfr
6732729Sdfr	case SETVAL:
674101774Salfred		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
675101774Salfred			goto done2;
676101774Salfred		mtx_lock(sema_mtxp);
677101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
678101774Salfred			goto done2;
67983366Sjulian		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W)))
68082607Sdillon			goto done2;
68182607Sdillon		if (semnum < 0 || semnum >= semaptr->sem_nsems) {
68282607Sdillon			error = EINVAL;
68382607Sdillon			goto done2;
68482607Sdillon		}
68584789Smr		if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) {
68684789Smr			error = ERANGE;
68784789Smr			goto done2;
68884789Smr		}
6892729Sdfr		semaptr->sem_base[semnum].semval = real_arg.val;
690101774Salfred		SEMUNDO_LOCK();
6912729Sdfr		semundo_clear(semid, semnum);
692101774Salfred		SEMUNDO_UNLOCK();
693100511Salfred		wakeup(semaptr);
6942729Sdfr		break;
6952729Sdfr
6962729Sdfr	case SETALL:
697101774Salfred		mtx_lock(sema_mtxp);
698101774Salfredraced:
699101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
70082607Sdillon			goto done2;
701101774Salfred		count = semaptr->sem_nsems;
702101774Salfred		mtx_unlock(sema_mtxp);
70382607Sdillon		if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
70482607Sdillon			goto done2;
705111119Simp		array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
706101774Salfred		copyin(real_arg.array, array, count * sizeof(*array));
707101774Salfred		if (error)
708101774Salfred			break;
709101774Salfred		mtx_lock(sema_mtxp);
710101774Salfred		if ((error = semvalid(uap->semid, semaptr)) != 0)
711101774Salfred			goto done2;
712101774Salfred		/* we could have raced? */
713101774Salfred		if (count != semaptr->sem_nsems) {
714101774Salfred			free(array, M_TEMP);
715101774Salfred			array = NULL;
716101774Salfred			goto raced;
717101774Salfred		}
718101774Salfred		if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W)))
719101774Salfred			goto done2;
7202729Sdfr		for (i = 0; i < semaptr->sem_nsems; i++) {
721101774Salfred			usval = array[i];
72284789Smr			if (usval > seminfo.semvmx) {
72384789Smr				error = ERANGE;
72484789Smr				break;
72584789Smr			}
72684789Smr			semaptr->sem_base[i].semval = usval;
7272729Sdfr		}
728101774Salfred		SEMUNDO_LOCK();
7292729Sdfr		semundo_clear(semid, -1);
730101774Salfred		SEMUNDO_UNLOCK();
731100511Salfred		wakeup(semaptr);
7322729Sdfr		break;
7332729Sdfr
7342729Sdfr	default:
73582607Sdillon		error = EINVAL;
73682607Sdillon		break;
7372729Sdfr	}
7382729Sdfr
73982607Sdillon	if (error == 0)
74083366Sjulian		td->td_retval[0] = rval;
74182607Sdillondone2:
742101774Salfred	if (mtx_owned(sema_mtxp))
743101774Salfred		mtx_unlock(sema_mtxp);
744101774Salfred	if (array != NULL)
745101774Salfred		free(array, M_TEMP);
74682607Sdillon	return(error);
7472729Sdfr}
7482729Sdfr
74912866Speter#ifndef _SYS_SYSPROTO_H_
7502729Sdfrstruct semget_args {
7512729Sdfr	key_t	key;
7522729Sdfr	int	nsems;
7532729Sdfr	int	semflg;
7542729Sdfr};
75512866Speter#endif
7562729Sdfr
75782607Sdillon/*
75882607Sdillon * MPSAFE
75982607Sdillon */
76012866Speterint
76183366Sjuliansemget(td, uap)
76283366Sjulian	struct thread *td;
763101350Salfred	struct semget_args *uap;
7642729Sdfr{
76582607Sdillon	int semid, error = 0;
7662729Sdfr	int key = uap->key;
7672729Sdfr	int nsems = uap->nsems;
7682729Sdfr	int semflg = uap->semflg;
76991703Sjhb	struct ucred *cred = td->td_ucred;
7702729Sdfr
771100523Salfred	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
77291703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
77391703Sjhb		return (ENOSYS);
77491703Sjhb
77582607Sdillon	mtx_lock(&Giant);
7762729Sdfr	if (key != IPC_PRIVATE) {
7772729Sdfr		for (semid = 0; semid < seminfo.semmni; semid++) {
7782729Sdfr			if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
7792729Sdfr			    sema[semid].sem_perm.key == key)
7802729Sdfr				break;
7812729Sdfr		}
7822729Sdfr		if (semid < seminfo.semmni) {
783100523Salfred			DPRINTF(("found public key\n"));
78483366Sjulian			if ((error = ipcperm(td, &sema[semid].sem_perm,
78582607Sdillon			    semflg & 0700))) {
78682607Sdillon				goto done2;
78782607Sdillon			}
7882729Sdfr			if (nsems > 0 && sema[semid].sem_nsems < nsems) {
789100523Salfred				DPRINTF(("too small\n"));
79082607Sdillon				error = EINVAL;
79182607Sdillon				goto done2;
7922729Sdfr			}
7932729Sdfr			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
794100523Salfred				DPRINTF(("not exclusive\n"));
79582607Sdillon				error = EEXIST;
79682607Sdillon				goto done2;
7972729Sdfr			}
7982729Sdfr			goto found;
7992729Sdfr		}
8002729Sdfr	}
8012729Sdfr
802100523Salfred	DPRINTF(("need to allocate the semid_ds\n"));
8032729Sdfr	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
8042729Sdfr		if (nsems <= 0 || nsems > seminfo.semmsl) {
805100523Salfred			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
806100523Salfred			    seminfo.semmsl));
80782607Sdillon			error = EINVAL;
80882607Sdillon			goto done2;
8092729Sdfr		}
8102729Sdfr		if (nsems > seminfo.semmns - semtot) {
811100523Salfred			DPRINTF((
812100523Salfred			    "not enough semaphores left (need %d, got %d)\n",
813100523Salfred			    nsems, seminfo.semmns - semtot));
81482607Sdillon			error = ENOSPC;
81582607Sdillon			goto done2;
8162729Sdfr		}
8172729Sdfr		for (semid = 0; semid < seminfo.semmni; semid++) {
8182729Sdfr			if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
8192729Sdfr				break;
8202729Sdfr		}
8212729Sdfr		if (semid == seminfo.semmni) {
822100523Salfred			DPRINTF(("no more semid_ds's available\n"));
82382607Sdillon			error = ENOSPC;
82482607Sdillon			goto done2;
8252729Sdfr		}
826100523Salfred		DPRINTF(("semid %d is available\n", semid));
8272729Sdfr		sema[semid].sem_perm.key = key;
8282729Sdfr		sema[semid].sem_perm.cuid = cred->cr_uid;
8292729Sdfr		sema[semid].sem_perm.uid = cred->cr_uid;
8302729Sdfr		sema[semid].sem_perm.cgid = cred->cr_gid;
8312729Sdfr		sema[semid].sem_perm.gid = cred->cr_gid;
8322729Sdfr		sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
8332729Sdfr		sema[semid].sem_perm.seq =
8342729Sdfr		    (sema[semid].sem_perm.seq + 1) & 0x7fff;
8352729Sdfr		sema[semid].sem_nsems = nsems;
8362729Sdfr		sema[semid].sem_otime = 0;
83734961Sphk		sema[semid].sem_ctime = time_second;
8382729Sdfr		sema[semid].sem_base = &sem[semtot];
8392729Sdfr		semtot += nsems;
8402729Sdfr		bzero(sema[semid].sem_base,
8412729Sdfr		    sizeof(sema[semid].sem_base[0])*nsems);
842100523Salfred		DPRINTF(("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
843100523Salfred		    &sem[semtot]));
8442729Sdfr	} else {
845100523Salfred		DPRINTF(("didn't find it and wasn't asked to create it\n"));
84682607Sdillon		error = ENOENT;
84782607Sdillon		goto done2;
8482729Sdfr	}
8492729Sdfr
8502729Sdfrfound:
85183366Sjulian	td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
85282607Sdillondone2:
85382607Sdillon	mtx_unlock(&Giant);
85482607Sdillon	return (error);
8552729Sdfr}
8562729Sdfr
85712866Speter#ifndef _SYS_SYSPROTO_H_
8582729Sdfrstruct semop_args {
8592729Sdfr	int	semid;
8602729Sdfr	struct	sembuf *sops;
861109829Salfred	size_t	nsops;
8622729Sdfr};
86312866Speter#endif
8642729Sdfr
86582607Sdillon/*
86682607Sdillon * MPSAFE
86782607Sdillon */
86812866Speterint
86983366Sjuliansemop(td, uap)
87083366Sjulian	struct thread *td;
871101350Salfred	struct semop_args *uap;
8722729Sdfr{
8732729Sdfr	int semid = uap->semid;
874109829Salfred	size_t nsops = uap->nsops;
875105429Salfred	struct sembuf *sops;
876101350Salfred	struct semid_ds *semaptr;
877101350Salfred	struct sembuf *sopptr = 0;
878101350Salfred	struct sem *semptr = 0;
87984789Smr	struct sem_undo *suptr;
880101774Salfred	struct mtx *sema_mtxp;
881110040Stjr	size_t i, j, k;
882109829Salfred	int error;
8833308Sphk	int do_wakeup, do_undos;
8842729Sdfr
885100523Salfred	DPRINTF(("call to semop(%d, 0x%x, %u)\n", semid, sops, nsops));
8862729Sdfr
88791703Sjhb	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
88891703Sjhb		return (ENOSYS);
88991703Sjhb
8902729Sdfr	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
8912729Sdfr
892101774Salfred	if (semid < 0 || semid >= seminfo.semmni)
893101774Salfred		return (EINVAL);
894101774Salfred
895101774Salfred	/* Allocate memory for sem_ops */
896101774Salfred	if (nsops > seminfo.semopm) {
897101774Salfred		DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm,
898101774Salfred		    nsops));
899101774Salfred		return (E2BIG);
90082607Sdillon	}
901111119Simp	sops = malloc(nsops * sizeof(sops[0]), M_SEM, M_WAITOK);
902101774Salfred	if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) {
903101774Salfred		DPRINTF(("error = %d from copyin(%08x, %08x, %d)\n", error,
904101774Salfred		    uap->sops, sops, nsops * sizeof(sops[0])));
905101774Salfred		free(sops, M_SEM);
906101774Salfred		return (error);
907101774Salfred	}
9082729Sdfr
9092729Sdfr	semaptr = &sema[semid];
910101774Salfred	sema_mtxp = &sema_mtx[semid];
911101774Salfred	mtx_lock(sema_mtxp);
91282607Sdillon	if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) {
91382607Sdillon		error = EINVAL;
91482607Sdillon		goto done2;
91582607Sdillon	}
91682607Sdillon	if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
91782607Sdillon		error = EINVAL;
91882607Sdillon		goto done2;
91982607Sdillon	}
92084789Smr	/*
92184789Smr	 * Initial pass thru sops to see what permissions are needed.
92284789Smr	 * Also perform any checks that don't need repeating on each
92384789Smr	 * attempt to satisfy the request vector.
92484789Smr	 */
92584789Smr	j = 0;		/* permission needed */
92684789Smr	do_undos = 0;
92784789Smr	for (i = 0; i < nsops; i++) {
92884789Smr		sopptr = &sops[i];
92984789Smr		if (sopptr->sem_num >= semaptr->sem_nsems) {
93084789Smr			error = EFBIG;
93184789Smr			goto done2;
93284789Smr		}
93384789Smr		if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0)
93484789Smr			do_undos = 1;
93584789Smr		j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A;
93684789Smr	}
93784789Smr
93884789Smr	if ((error = ipcperm(td, &semaptr->sem_perm, j))) {
939100523Salfred		DPRINTF(("error = %d from ipaccess\n", error));
94082607Sdillon		goto done2;
9412729Sdfr	}
9422729Sdfr
9438876Srgrimes	/*
9442729Sdfr	 * Loop trying to satisfy the vector of requests.
9452729Sdfr	 * If we reach a point where we must wait, any requests already
9462729Sdfr	 * performed are rolled back and we go to sleep until some other
9472729Sdfr	 * process wakes us up.  At this point, we start all over again.
9482729Sdfr	 *
9492729Sdfr	 * This ensures that from the perspective of other tasks, a set
9502729Sdfr	 * of requests is atomic (never partially satisfied).
9512729Sdfr	 */
9522729Sdfr	for (;;) {
9532729Sdfr		do_wakeup = 0;
95484789Smr		error = 0;	/* error return if necessary */
9552729Sdfr
9562729Sdfr		for (i = 0; i < nsops; i++) {
9572729Sdfr			sopptr = &sops[i];
9582729Sdfr			semptr = &semaptr->sem_base[sopptr->sem_num];
9592729Sdfr
960100523Salfred			DPRINTF((
961100523Salfred			    "semop:  semaptr=%x, sem_base=%x, "
962100523Salfred			    "semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
9632729Sdfr			    semaptr, semaptr->sem_base, semptr,
9642729Sdfr			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
965100523Salfred			    (sopptr->sem_flg & IPC_NOWAIT) ?
966100523Salfred			    "nowait" : "wait"));
9672729Sdfr
9682729Sdfr			if (sopptr->sem_op < 0) {
9692729Sdfr				if (semptr->semval + sopptr->sem_op < 0) {
970100523Salfred					DPRINTF(("semop:  can't do it now\n"));
9712729Sdfr					break;
9722729Sdfr				} else {
9732729Sdfr					semptr->semval += sopptr->sem_op;
9742729Sdfr					if (semptr->semval == 0 &&
9752729Sdfr					    semptr->semzcnt > 0)
9762729Sdfr						do_wakeup = 1;
9772729Sdfr				}
9782729Sdfr			} else if (sopptr->sem_op == 0) {
97984789Smr				if (semptr->semval != 0) {
980100523Salfred					DPRINTF(("semop:  not zero now\n"));
9812729Sdfr					break;
9822729Sdfr				}
98384789Smr			} else if (semptr->semval + sopptr->sem_op >
98484789Smr			    seminfo.semvmx) {
98584789Smr				error = ERANGE;
98684789Smr				break;
9872729Sdfr			} else {
9882729Sdfr				if (semptr->semncnt > 0)
9892729Sdfr					do_wakeup = 1;
9902729Sdfr				semptr->semval += sopptr->sem_op;
9912729Sdfr			}
9922729Sdfr		}
9932729Sdfr
9942729Sdfr		/*
9952729Sdfr		 * Did we get through the entire vector?
9962729Sdfr		 */
9972729Sdfr		if (i >= nsops)
9982729Sdfr			goto done;
9992729Sdfr
10002729Sdfr		/*
10012729Sdfr		 * No ... rollback anything that we've already done
10022729Sdfr		 */
1003100523Salfred		DPRINTF(("semop:  rollback 0 through %d\n", i-1));
10042729Sdfr		for (j = 0; j < i; j++)
10052729Sdfr			semaptr->sem_base[sops[j].sem_num].semval -=
10062729Sdfr			    sops[j].sem_op;
10072729Sdfr
100884789Smr		/* If we detected an error, return it */
100984789Smr		if (error != 0)
101084789Smr			goto done2;
101184789Smr
10122729Sdfr		/*
10132729Sdfr		 * If the request that we couldn't satisfy has the
10142729Sdfr		 * NOWAIT flag set then return with EAGAIN.
10152729Sdfr		 */
101682607Sdillon		if (sopptr->sem_flg & IPC_NOWAIT) {
101782607Sdillon			error = EAGAIN;
101882607Sdillon			goto done2;
101982607Sdillon		}
10202729Sdfr
10212729Sdfr		if (sopptr->sem_op == 0)
10222729Sdfr			semptr->semzcnt++;
10232729Sdfr		else
10242729Sdfr			semptr->semncnt++;
10252729Sdfr
1026100523Salfred		DPRINTF(("semop:  good night!\n"));
1027101774Salfred		error = msleep(semaptr, sema_mtxp, (PZERO - 4) | PCATCH,
1028101774Salfred		    "semwait", 0);
1029100523Salfred		DPRINTF(("semop:  good morning (error=%d)!\n", error));
10302729Sdfr
103182607Sdillon		if (error != 0) {
103282607Sdillon			error = EINTR;
103382607Sdillon			goto done2;
103482607Sdillon		}
1035100523Salfred		DPRINTF(("semop:  good morning!\n"));
10362729Sdfr
10372729Sdfr		/*
10382729Sdfr		 * Make sure that the semaphore still exists
10392729Sdfr		 */
10402729Sdfr		if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
104182607Sdillon		    semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
104282607Sdillon			error = EIDRM;
104382607Sdillon			goto done2;
104482607Sdillon		}
10452729Sdfr
10462729Sdfr		/*
10472729Sdfr		 * The semaphore is still alive.  Readjust the count of
10482729Sdfr		 * waiting processes.
10492729Sdfr		 */
10502729Sdfr		if (sopptr->sem_op == 0)
10512729Sdfr			semptr->semzcnt--;
10522729Sdfr		else
10532729Sdfr			semptr->semncnt--;
10542729Sdfr	}
10552729Sdfr
10562729Sdfrdone:
10572729Sdfr	/*
10582729Sdfr	 * Process any SEM_UNDO requests.
10592729Sdfr	 */
10602729Sdfr	if (do_undos) {
1061101774Salfred		SEMUNDO_LOCK();
106284789Smr		suptr = NULL;
10632729Sdfr		for (i = 0; i < nsops; i++) {
10642729Sdfr			/*
10652729Sdfr			 * We only need to deal with SEM_UNDO's for non-zero
10662729Sdfr			 * op's.
10672729Sdfr			 */
10682729Sdfr			int adjval;
10692729Sdfr
10702729Sdfr			if ((sops[i].sem_flg & SEM_UNDO) == 0)
10712729Sdfr				continue;
10722729Sdfr			adjval = sops[i].sem_op;
10732729Sdfr			if (adjval == 0)
10742729Sdfr				continue;
107583366Sjulian			error = semundo_adjust(td, &suptr, semid,
10762729Sdfr			    sops[i].sem_num, -adjval);
107782607Sdillon			if (error == 0)
10782729Sdfr				continue;
10792729Sdfr
10802729Sdfr			/*
10812729Sdfr			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
10822729Sdfr			 * Rollback the adjustments to this point and then
10832729Sdfr			 * rollback the semaphore ups and down so we can return
10842729Sdfr			 * with an error with all structures restored.  We
10852729Sdfr			 * rollback the undo's in the exact reverse order that
10862729Sdfr			 * we applied them.  This guarantees that we won't run
10872729Sdfr			 * out of space as we roll things back out.
10882729Sdfr			 */
1089110040Stjr			for (j = 0; j < i; j++) {
1090110040Stjr				k = i - j - 1;
1091110040Stjr				if ((sops[k].sem_flg & SEM_UNDO) == 0)
10922729Sdfr					continue;
1093110040Stjr				adjval = sops[k].sem_op;
10942729Sdfr				if (adjval == 0)
10952729Sdfr					continue;
109683366Sjulian				if (semundo_adjust(td, &suptr, semid,
1097110040Stjr				    sops[k].sem_num, adjval) != 0)
10982729Sdfr					panic("semop - can't undo undos");
10992729Sdfr			}
11002729Sdfr
11012729Sdfr			for (j = 0; j < nsops; j++)
11022729Sdfr				semaptr->sem_base[sops[j].sem_num].semval -=
11032729Sdfr				    sops[j].sem_op;
11042729Sdfr
1105100523Salfred			DPRINTF(("error = %d from semundo_adjust\n", error));
1106101774Salfred			SEMUNDO_UNLOCK();
110782607Sdillon			goto done2;
11082729Sdfr		} /* loop through the sops */
1109101774Salfred		SEMUNDO_UNLOCK();
11102729Sdfr	} /* if (do_undos) */
11112729Sdfr
111284789Smr	/* We're definitely done - set the sempid's and time */
11132729Sdfr	for (i = 0; i < nsops; i++) {
11142729Sdfr		sopptr = &sops[i];
11152729Sdfr		semptr = &semaptr->sem_base[sopptr->sem_num];
111683366Sjulian		semptr->sempid = td->td_proc->p_pid;
11172729Sdfr	}
111884789Smr	semaptr->sem_otime = time_second;
11192729Sdfr
112084789Smr	/*
112184789Smr	 * Do a wakeup if any semaphore was up'd whilst something was
112284789Smr	 * sleeping on it.
112384789Smr	 */
11242729Sdfr	if (do_wakeup) {
1125100523Salfred		DPRINTF(("semop:  doing wakeup\n"));
1126100511Salfred		wakeup(semaptr);
1127100523Salfred		DPRINTF(("semop:  back from wakeup\n"));
11282729Sdfr	}
1129100523Salfred	DPRINTF(("semop:  done\n"));
113083366Sjulian	td->td_retval[0] = 0;
113182607Sdillondone2:
1132101774Salfred	mtx_unlock(sema_mtxp);
1133105429Salfred	free(sops, M_SEM);
113482607Sdillon	return (error);
11352729Sdfr}
11362729Sdfr
11372729Sdfr/*
11382729Sdfr * Go through the undo structures for this process and apply the adjustments to
11392729Sdfr * semaphores.
11402729Sdfr */
114169449Salfredstatic void
114269449Salfredsemexit_myhook(p)
11432729Sdfr	struct proc *p;
11442729Sdfr{
1145101350Salfred	struct sem_undo *suptr;
1146101350Salfred	struct sem_undo **supptr;
11472729Sdfr
11482729Sdfr	/*
11492729Sdfr	 * Go through the chain of undo vectors looking for one
11502729Sdfr	 * associated with this process.
11512729Sdfr	 */
1152101774Salfred	SEMUNDO_LOCK();
1153101774Salfred	SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
11542729Sdfr		if (suptr->un_proc == p)
11552729Sdfr			break;
11562729Sdfr	}
1157101774Salfred	SEMUNDO_UNLOCK();
11582729Sdfr
11592729Sdfr	if (suptr == NULL)
116059828Speter		return;
11612729Sdfr
1162100523Salfred	DPRINTF(("proc @%08x has undo structure with %d entries\n", p,
1163100523Salfred	    suptr->un_cnt));
11642729Sdfr
11652729Sdfr	/*
11662729Sdfr	 * If there are any active undo elements then process them.
11672729Sdfr	 */
11682729Sdfr	if (suptr->un_cnt > 0) {
11692729Sdfr		int ix;
11702729Sdfr
11712729Sdfr		for (ix = 0; ix < suptr->un_cnt; ix++) {
11722729Sdfr			int semid = suptr->un_ent[ix].un_id;
11732729Sdfr			int semnum = suptr->un_ent[ix].un_num;
11742729Sdfr			int adjval = suptr->un_ent[ix].un_adjval;
11752729Sdfr			struct semid_ds *semaptr;
1176101774Salfred			struct mtx *sema_mtxp;
11772729Sdfr
11782729Sdfr			semaptr = &sema[semid];
1179101774Salfred			sema_mtxp = &sema_mtx[semid];
1180101774Salfred			mtx_lock(sema_mtxp);
1181101774Salfred			SEMUNDO_LOCK();
11822729Sdfr			if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
11832729Sdfr				panic("semexit - semid not allocated");
11842729Sdfr			if (semnum >= semaptr->sem_nsems)
11852729Sdfr				panic("semexit - semnum out of range");
11862729Sdfr
1187100523Salfred			DPRINTF((
1188100523Salfred			    "semexit:  %08x id=%d num=%d(adj=%d) ; sem=%d\n",
11892729Sdfr			    suptr->un_proc, suptr->un_ent[ix].un_id,
11902729Sdfr			    suptr->un_ent[ix].un_num,
11912729Sdfr			    suptr->un_ent[ix].un_adjval,
1192100523Salfred			    semaptr->sem_base[semnum].semval));
11932729Sdfr
11942729Sdfr			if (adjval < 0) {
11952729Sdfr				if (semaptr->sem_base[semnum].semval < -adjval)
11962729Sdfr					semaptr->sem_base[semnum].semval = 0;
11972729Sdfr				else
11982729Sdfr					semaptr->sem_base[semnum].semval +=
11992729Sdfr					    adjval;
12002729Sdfr			} else
12012729Sdfr				semaptr->sem_base[semnum].semval += adjval;
12022729Sdfr
1203100511Salfred			wakeup(semaptr);
1204100523Salfred			DPRINTF(("semexit:  back from wakeup\n"));
1205101774Salfred			mtx_unlock(sema_mtxp);
1206101774Salfred			SEMUNDO_UNLOCK();
12072729Sdfr		}
12082729Sdfr	}
12092729Sdfr
12102729Sdfr	/*
12112729Sdfr	 * Deallocate the undo vector.
12122729Sdfr	 */
1213100523Salfred	DPRINTF(("removing vector\n"));
12142729Sdfr	suptr->un_proc = NULL;
1215101774Salfred	*supptr = SLIST_NEXT(suptr, un_next);
12162729Sdfr}
121777461Sdd
121877461Sddstatic int
121977461Sddsysctl_sema(SYSCTL_HANDLER_ARGS)
122077461Sdd{
122177461Sdd
122277461Sdd	return (SYSCTL_OUT(req, sema,
122377461Sdd	    sizeof(struct semid_ds) * seminfo.semmni));
122477461Sdd}
1225