xref: /minix3/minix/servers/ipc/sem.c (revision 534584945c0f50b9816e7051003856bd31bf8c65)
1433d6423SLionel Sambuc #include "inc.h"
2433d6423SLionel Sambuc 
356dc79ceSDavid van Moolenbroek struct sem_struct;
456dc79ceSDavid van Moolenbroek 
556dc79ceSDavid van Moolenbroek /* IPC-server process table, currently used for semaphores only. */
656dc79ceSDavid van Moolenbroek struct iproc {
756dc79ceSDavid van Moolenbroek 	struct sem_struct *ip_sem;	/* affected semaphore set, or NULL */
856dc79ceSDavid van Moolenbroek 	struct sembuf *ip_sops;		/* pending operations (malloc'ed) */
956dc79ceSDavid van Moolenbroek 	unsigned int ip_nsops;		/* number of pending operations */
1056dc79ceSDavid van Moolenbroek 	struct sembuf *ip_blkop;	/* pointer to operation that blocked */
1156dc79ceSDavid van Moolenbroek 	endpoint_t ip_endpt;		/* process endpoint */
1256dc79ceSDavid van Moolenbroek 	pid_t ip_pid;			/* process PID */
1356dc79ceSDavid van Moolenbroek 	TAILQ_ENTRY(iproc) ip_next;	/* next waiting process */
1456dc79ceSDavid van Moolenbroek } iproc[NR_PROCS];
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc struct semaphore {
17433d6423SLionel Sambuc 	unsigned short semval;		/* semaphore value */
18433d6423SLionel Sambuc 	unsigned short semzcnt;		/* # waiting for zero */
19433d6423SLionel Sambuc 	unsigned short semncnt;		/* # waiting for increase */
20433d6423SLionel Sambuc 	pid_t sempid;			/* process that did last op */
21433d6423SLionel Sambuc };
22433d6423SLionel Sambuc 
2356dc79ceSDavid van Moolenbroek /*
2456dc79ceSDavid van Moolenbroek  * For the list of waiting processes, we use a doubly linked tail queue.  In
2556dc79ceSDavid van Moolenbroek  * order to maintain a basic degree of fairness, we keep the pending processes
2656dc79ceSDavid van Moolenbroek  * in FCFS (well, at least first tested) order, which means we need to be able
2756dc79ceSDavid van Moolenbroek  * to add new processes at the end of the list.  In order to remove waiting
2856dc79ceSDavid van Moolenbroek  * processes O(1) instead of O(n) we need a doubly linked list; in the common
2956dc79ceSDavid van Moolenbroek  * case we do have the element's predecessor, but STAILQ_REMOVE is O(n) anyway
3056dc79ceSDavid van Moolenbroek  * and NetBSD has no STAILQ_REMOVE_AFTER yet.
3156dc79ceSDavid van Moolenbroek  *
3256dc79ceSDavid van Moolenbroek  * We use one list per semaphore set: semop(2) affects only one semaphore set,
3356dc79ceSDavid van Moolenbroek  * but it may involve operations on multiple semaphores within the set.  While
3456dc79ceSDavid van Moolenbroek  * it is possible to recheck only semaphores that were affected by a particular
3556dc79ceSDavid van Moolenbroek  * operation, and associate waiting lists to individual semaphores, the number
3656dc79ceSDavid van Moolenbroek  * of expected waiting processes is currently not high enough to justify the
3756dc79ceSDavid van Moolenbroek  * extra complexity of such an implementation.
3856dc79ceSDavid van Moolenbroek  */
39433d6423SLionel Sambuc struct sem_struct {
40433d6423SLionel Sambuc 	struct semid_ds semid_ds;
41433d6423SLionel Sambuc 	struct semaphore sems[SEMMSL];
4256dc79ceSDavid van Moolenbroek 	TAILQ_HEAD(waiters, iproc) waiters;
43433d6423SLionel Sambuc };
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc static struct sem_struct sem_list[SEMMNI];
464d272e5aSDavid van Moolenbroek static unsigned int sem_list_nr = 0; /* highest in-use slot number plus one */
47433d6423SLionel Sambuc 
4856dc79ceSDavid van Moolenbroek /*
4956dc79ceSDavid van Moolenbroek  * Find a semaphore set by key.  The given key must not be IPC_PRIVATE.  Return
5056dc79ceSDavid van Moolenbroek  * a pointer to the semaphore set if found, or NULL otherwise.
5156dc79ceSDavid van Moolenbroek  */
520baafa0eSDavid van Moolenbroek static struct sem_struct *
sem_find_key(key_t key)530baafa0eSDavid van Moolenbroek sem_find_key(key_t key)
54433d6423SLionel Sambuc {
554d272e5aSDavid van Moolenbroek 	unsigned int i;
564d272e5aSDavid van Moolenbroek 
574d272e5aSDavid van Moolenbroek 	for (i = 0; i < sem_list_nr; i++) {
584d272e5aSDavid van Moolenbroek 		if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
594d272e5aSDavid van Moolenbroek 			continue;
604d272e5aSDavid van Moolenbroek 		if (sem_list[i].semid_ds.sem_perm._key == key)
614d272e5aSDavid van Moolenbroek 			return &sem_list[i];
624d272e5aSDavid van Moolenbroek 	}
630baafa0eSDavid van Moolenbroek 
64433d6423SLionel Sambuc 	return NULL;
65433d6423SLionel Sambuc }
66433d6423SLionel Sambuc 
6756dc79ceSDavid van Moolenbroek /*
6856dc79ceSDavid van Moolenbroek  * Find a semaphore set by identifier.  Return a pointer to the semaphore set
6956dc79ceSDavid van Moolenbroek  * if found, or NULL otherwise.
7056dc79ceSDavid van Moolenbroek  */
710baafa0eSDavid van Moolenbroek static struct sem_struct *
sem_find_id(int id)720baafa0eSDavid van Moolenbroek sem_find_id(int id)
73433d6423SLionel Sambuc {
744d272e5aSDavid van Moolenbroek 	struct sem_struct *sem;
754d272e5aSDavid van Moolenbroek 	unsigned int i;
764d272e5aSDavid van Moolenbroek 
774d272e5aSDavid van Moolenbroek 	i = IPCID_TO_IX(id);
784d272e5aSDavid van Moolenbroek 	if (i >= sem_list_nr)
79433d6423SLionel Sambuc 		return NULL;
804d272e5aSDavid van Moolenbroek 
814d272e5aSDavid van Moolenbroek 	sem = &sem_list[i];
824d272e5aSDavid van Moolenbroek 	if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
834d272e5aSDavid van Moolenbroek 		return NULL;
844d272e5aSDavid van Moolenbroek 	if (sem->semid_ds.sem_perm._seq != IPCID_TO_SEQ(id))
854d272e5aSDavid van Moolenbroek 		return NULL;
864d272e5aSDavid van Moolenbroek 	return sem;
87433d6423SLionel Sambuc }
88433d6423SLionel Sambuc 
8956dc79ceSDavid van Moolenbroek /*
9056dc79ceSDavid van Moolenbroek  * Implementation of the semget(2) system call.
9156dc79ceSDavid van Moolenbroek  */
920baafa0eSDavid van Moolenbroek int
do_semget(message * m)930baafa0eSDavid van Moolenbroek do_semget(message * m)
94433d6423SLionel Sambuc {
95433d6423SLionel Sambuc 	struct sem_struct *sem;
964d272e5aSDavid van Moolenbroek 	unsigned int i, seq;
974d272e5aSDavid van Moolenbroek 	key_t key;
984d272e5aSDavid van Moolenbroek 	int nsems, flag;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc 	key = m->m_lc_ipc_semget.key;
101433d6423SLionel Sambuc 	nsems = m->m_lc_ipc_semget.nr;
102433d6423SLionel Sambuc 	flag = m->m_lc_ipc_semget.flag;
103433d6423SLionel Sambuc 
10456dc79ceSDavid van Moolenbroek 	if (key != IPC_PRIVATE && (sem = sem_find_key(key)) != NULL) {
105433d6423SLionel Sambuc 		if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
106433d6423SLionel Sambuc 			return EEXIST;
1070baafa0eSDavid van Moolenbroek 		if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, flag))
108433d6423SLionel Sambuc 			return EACCES;
109433d6423SLionel Sambuc 		if (nsems > sem->semid_ds.sem_nsems)
110433d6423SLionel Sambuc 			return EINVAL;
1114d272e5aSDavid van Moolenbroek 		i = sem - sem_list;
112433d6423SLionel Sambuc 	} else {
11356dc79ceSDavid van Moolenbroek 		if (key != IPC_PRIVATE && !(flag & IPC_CREAT))
114433d6423SLionel Sambuc 			return ENOENT;
11556dc79ceSDavid van Moolenbroek 		if (nsems <= 0 || nsems > SEMMSL)
116433d6423SLionel Sambuc 			return EINVAL;
1174d272e5aSDavid van Moolenbroek 
1184d272e5aSDavid van Moolenbroek 		/* Find a free entry. */
1194d272e5aSDavid van Moolenbroek 		for (i = 0; i < __arraycount(sem_list); i++)
1204d272e5aSDavid van Moolenbroek 			if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
1214d272e5aSDavid van Moolenbroek 				break;
1224d272e5aSDavid van Moolenbroek 		if (i == __arraycount(sem_list))
123433d6423SLionel Sambuc 			return ENOSPC;
124433d6423SLionel Sambuc 
1254d272e5aSDavid van Moolenbroek 		/* Initialize the entry. */
1264d272e5aSDavid van Moolenbroek 		sem = &sem_list[i];
1274d272e5aSDavid van Moolenbroek 		seq = sem->semid_ds.sem_perm._seq;
1280baafa0eSDavid van Moolenbroek 		memset(sem, 0, sizeof(*sem));
1294d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_perm._key = key;
130433d6423SLionel Sambuc 		sem->semid_ds.sem_perm.cuid =
1310baafa0eSDavid van Moolenbroek 		    sem->semid_ds.sem_perm.uid = getnuid(m->m_source);
132433d6423SLionel Sambuc 		sem->semid_ds.sem_perm.cgid =
1330baafa0eSDavid van Moolenbroek 		    sem->semid_ds.sem_perm.gid = getngid(m->m_source);
1344d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_perm.mode = SEM_ALLOC | (flag & ACCESSPERMS);
1354d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_perm._seq = (seq + 1) & 0x7fff;
136433d6423SLionel Sambuc 		sem->semid_ds.sem_nsems = nsems;
137433d6423SLionel Sambuc 		sem->semid_ds.sem_otime = 0;
1384d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_ctime = clock_time(NULL);
13956dc79ceSDavid van Moolenbroek 		TAILQ_INIT(&sem->waiters);
140433d6423SLionel Sambuc 
1414d272e5aSDavid van Moolenbroek 		assert(i <= sem_list_nr);
1425b1db956SDavid van Moolenbroek 		if (i == sem_list_nr) {
1435b1db956SDavid van Moolenbroek 			/*
1445b1db956SDavid van Moolenbroek 			 * If no semaphore sets were allocated before,
1455b1db956SDavid van Moolenbroek 			 * subscribe to process events now.
1465b1db956SDavid van Moolenbroek 			 */
1475b1db956SDavid van Moolenbroek 			if (sem_list_nr == 0)
1485b1db956SDavid van Moolenbroek 				update_sem_sub(TRUE /*want_events*/);
1495b1db956SDavid van Moolenbroek 
150433d6423SLionel Sambuc 			sem_list_nr++;
151433d6423SLionel Sambuc 		}
1525b1db956SDavid van Moolenbroek 	}
153433d6423SLionel Sambuc 
1544d272e5aSDavid van Moolenbroek 	m->m_lc_ipc_semget.retid = IXSEQ_TO_IPCID(i, sem->semid_ds.sem_perm);
155433d6423SLionel Sambuc 	return OK;
156433d6423SLionel Sambuc }
157433d6423SLionel Sambuc 
15856dc79ceSDavid van Moolenbroek /*
15956dc79ceSDavid van Moolenbroek  * Increase the proper suspension count (semncnt or semzcnt) of the semaphore
16056dc79ceSDavid van Moolenbroek  * on which the given process is blocked.
16156dc79ceSDavid van Moolenbroek  */
16256dc79ceSDavid van Moolenbroek static void
inc_susp_count(struct iproc * ip)16356dc79ceSDavid van Moolenbroek inc_susp_count(struct iproc * ip)
16456dc79ceSDavid van Moolenbroek {
16556dc79ceSDavid van Moolenbroek 	struct sembuf *blkop;
16656dc79ceSDavid van Moolenbroek 	struct semaphore *sp;
16756dc79ceSDavid van Moolenbroek 
16856dc79ceSDavid van Moolenbroek 	blkop = ip->ip_blkop;
16956dc79ceSDavid van Moolenbroek 	sp = &ip->ip_sem->sems[blkop->sem_num];
17056dc79ceSDavid van Moolenbroek 
17156dc79ceSDavid van Moolenbroek 	if (blkop->sem_op != 0) {
17256dc79ceSDavid van Moolenbroek 		assert(sp->semncnt < USHRT_MAX);
17356dc79ceSDavid van Moolenbroek 		sp->semncnt++;
17456dc79ceSDavid van Moolenbroek 	} else {
17556dc79ceSDavid van Moolenbroek 		assert(sp->semncnt < USHRT_MAX);
17656dc79ceSDavid van Moolenbroek 		sp->semzcnt++;
17756dc79ceSDavid van Moolenbroek 	}
17856dc79ceSDavid van Moolenbroek }
17956dc79ceSDavid van Moolenbroek 
18056dc79ceSDavid van Moolenbroek /*
18156dc79ceSDavid van Moolenbroek  * Decrease the proper suspension count (semncnt or semzcnt) of the semaphore
18256dc79ceSDavid van Moolenbroek  * on which the given process is blocked.
18356dc79ceSDavid van Moolenbroek  */
18456dc79ceSDavid van Moolenbroek static void
dec_susp_count(struct iproc * ip)18556dc79ceSDavid van Moolenbroek dec_susp_count(struct iproc * ip)
18656dc79ceSDavid van Moolenbroek {
18756dc79ceSDavid van Moolenbroek 	struct sembuf *blkop;
18856dc79ceSDavid van Moolenbroek 	struct semaphore *sp;
18956dc79ceSDavid van Moolenbroek 
19056dc79ceSDavid van Moolenbroek 	blkop = ip->ip_blkop;
19156dc79ceSDavid van Moolenbroek 	sp = &ip->ip_sem->sems[blkop->sem_num];
19256dc79ceSDavid van Moolenbroek 
19356dc79ceSDavid van Moolenbroek 	if (blkop->sem_op != 0) {
19456dc79ceSDavid van Moolenbroek 		assert(sp->semncnt > 0);
19556dc79ceSDavid van Moolenbroek 		sp->semncnt--;
19656dc79ceSDavid van Moolenbroek 	} else {
19756dc79ceSDavid van Moolenbroek 		assert(sp->semzcnt > 0);
19856dc79ceSDavid van Moolenbroek 		sp->semzcnt--;
19956dc79ceSDavid van Moolenbroek 	}
20056dc79ceSDavid van Moolenbroek }
20156dc79ceSDavid van Moolenbroek 
20256dc79ceSDavid van Moolenbroek /*
20356dc79ceSDavid van Moolenbroek  * Send a reply for a semop(2) call suspended earlier, thus waking up the
20456dc79ceSDavid van Moolenbroek  * process.
20556dc79ceSDavid van Moolenbroek  */
2060baafa0eSDavid van Moolenbroek static void
send_reply(endpoint_t who,int ret)2070baafa0eSDavid van Moolenbroek send_reply(endpoint_t who, int ret)
208433d6423SLionel Sambuc {
209433d6423SLionel Sambuc 	message m;
210433d6423SLionel Sambuc 
2110baafa0eSDavid van Moolenbroek 	memset(&m, 0, sizeof(m));
212433d6423SLionel Sambuc 	m.m_type = ret;
2130baafa0eSDavid van Moolenbroek 
214433d6423SLionel Sambuc 	ipc_sendnb(who, &m);
215433d6423SLionel Sambuc }
216433d6423SLionel Sambuc 
21756dc79ceSDavid van Moolenbroek /*
21856dc79ceSDavid van Moolenbroek  * Satisfy or cancel the semop(2) call on which the given process is blocked,
21956dc79ceSDavid van Moolenbroek  * and send the given reply code (OK or a negative error code) to wake it up,
22056dc79ceSDavid van Moolenbroek  * unless the given code is EDONTREPLY.
22156dc79ceSDavid van Moolenbroek  */
2220baafa0eSDavid van Moolenbroek static void
complete_semop(struct iproc * ip,int code)22356dc79ceSDavid van Moolenbroek complete_semop(struct iproc * ip, int code)
224433d6423SLionel Sambuc {
22556dc79ceSDavid van Moolenbroek 	struct sem_struct *sem;
226433d6423SLionel Sambuc 
22756dc79ceSDavid van Moolenbroek 	sem = ip->ip_sem;
228433d6423SLionel Sambuc 
22956dc79ceSDavid van Moolenbroek 	assert(sem != NULL);
2300baafa0eSDavid van Moolenbroek 
23156dc79ceSDavid van Moolenbroek 	TAILQ_REMOVE(&sem->waiters, ip, ip_next);
2320baafa0eSDavid van Moolenbroek 
23356dc79ceSDavid van Moolenbroek 	dec_susp_count(ip);
23456dc79ceSDavid van Moolenbroek 
23556dc79ceSDavid van Moolenbroek 	assert(ip->ip_sops != NULL);
23656dc79ceSDavid van Moolenbroek 	free(ip->ip_sops);
23756dc79ceSDavid van Moolenbroek 
23856dc79ceSDavid van Moolenbroek 	ip->ip_sops = NULL;
23956dc79ceSDavid van Moolenbroek 	ip->ip_blkop = NULL;
24056dc79ceSDavid van Moolenbroek 	ip->ip_sem = NULL;
24156dc79ceSDavid van Moolenbroek 
24256dc79ceSDavid van Moolenbroek 	if (code != EDONTREPLY)
24356dc79ceSDavid van Moolenbroek 		send_reply(ip->ip_endpt, code);
2440baafa0eSDavid van Moolenbroek }
24556dc79ceSDavid van Moolenbroek 
24656dc79ceSDavid van Moolenbroek /*
24756dc79ceSDavid van Moolenbroek  * Free up the given semaphore set.  This includes cancelling any blocking
24856dc79ceSDavid van Moolenbroek  * semop(2) calls on any of its semaphores.
24956dc79ceSDavid van Moolenbroek  */
25056dc79ceSDavid van Moolenbroek static void
remove_set(struct sem_struct * sem)25156dc79ceSDavid van Moolenbroek remove_set(struct sem_struct * sem)
25256dc79ceSDavid van Moolenbroek {
25356dc79ceSDavid van Moolenbroek 	struct iproc *ip;
25456dc79ceSDavid van Moolenbroek 
25556dc79ceSDavid van Moolenbroek 	/*
25656dc79ceSDavid van Moolenbroek 	 * Cancel all semop(2) operations on this semaphore set, with an EIDRM
25756dc79ceSDavid van Moolenbroek 	 * reply code.
25856dc79ceSDavid van Moolenbroek 	 */
25956dc79ceSDavid van Moolenbroek 	while (!TAILQ_EMPTY(&sem->waiters)) {
26056dc79ceSDavid van Moolenbroek 		ip = TAILQ_FIRST(&sem->waiters);
26156dc79ceSDavid van Moolenbroek 
26256dc79ceSDavid van Moolenbroek 		complete_semop(ip, EIDRM);
263433d6423SLionel Sambuc 	}
264433d6423SLionel Sambuc 
2654d272e5aSDavid van Moolenbroek 	/* Mark the entry as free. */
2664d272e5aSDavid van Moolenbroek 	sem->semid_ds.sem_perm.mode &= ~SEM_ALLOC;
267433d6423SLionel Sambuc 
2684d272e5aSDavid van Moolenbroek 	/*
2694d272e5aSDavid van Moolenbroek 	 * This may have been the last in-use slot in the list.  Ensure that
2704d272e5aSDavid van Moolenbroek 	 * sem_list_nr again equals the highest in-use slot number plus one.
2714d272e5aSDavid van Moolenbroek 	 */
2724d272e5aSDavid van Moolenbroek 	while (sem_list_nr > 0 &&
2734d272e5aSDavid van Moolenbroek 	    !(sem_list[sem_list_nr - 1].semid_ds.sem_perm.mode & SEM_ALLOC))
2744d272e5aSDavid van Moolenbroek 		sem_list_nr--;
2755b1db956SDavid van Moolenbroek 
2765b1db956SDavid van Moolenbroek 	/*
2775b1db956SDavid van Moolenbroek 	 * If this was our last semaphore set, unsubscribe from process events.
2785b1db956SDavid van Moolenbroek 	 */
2795b1db956SDavid van Moolenbroek 	if (sem_list_nr == 0)
2805b1db956SDavid van Moolenbroek 		update_sem_sub(FALSE /*want_events*/);
281433d6423SLionel Sambuc }
282433d6423SLionel Sambuc 
28356dc79ceSDavid van Moolenbroek /*
28456dc79ceSDavid van Moolenbroek  * Try to perform a set of semaphore operations, as given by semop(2), on a
28556dc79ceSDavid van Moolenbroek  * semaphore set.  The entire action must be atomic, i.e., either succeed in
28656dc79ceSDavid van Moolenbroek  * its entirety or fail without making any changes.  Return OK on success, in
28756dc79ceSDavid van Moolenbroek  * which case the PIDs of all affected semaphores will be updated to the given
28856dc79ceSDavid van Moolenbroek  * 'pid' value, and the semaphore set's sem_otime will be updated as well.
28956dc79ceSDavid van Moolenbroek  * Return SUSPEND if the call should be suspended, in which case 'blkop' will
29056dc79ceSDavid van Moolenbroek  * be set to a pointer to the operation causing the call to block.  Return an
29156dc79ceSDavid van Moolenbroek  * error code if the call failed altogether.
29256dc79ceSDavid van Moolenbroek  */
29356dc79ceSDavid van Moolenbroek static int
try_semop(struct sem_struct * sem,struct sembuf * sops,unsigned int nsops,pid_t pid,struct sembuf ** blkop)29456dc79ceSDavid van Moolenbroek try_semop(struct sem_struct *sem, struct sembuf *sops, unsigned int nsops,
29556dc79ceSDavid van Moolenbroek 	pid_t pid, struct sembuf ** blkop)
296433d6423SLionel Sambuc {
29756dc79ceSDavid van Moolenbroek 	struct semaphore *sp;
29856dc79ceSDavid van Moolenbroek 	struct sembuf *op;
2994d272e5aSDavid van Moolenbroek 	unsigned int i;
30056dc79ceSDavid van Moolenbroek 	int r;
301433d6423SLionel Sambuc 
30256dc79ceSDavid van Moolenbroek 	/*
30356dc79ceSDavid van Moolenbroek 	 * The operation must be processed atomically.  However, it must also
30456dc79ceSDavid van Moolenbroek 	 * be processed "in array order," which we assume to mean that while
30556dc79ceSDavid van Moolenbroek 	 * processing one operation, the changes of the previous operations
30656dc79ceSDavid van Moolenbroek 	 * must be taken into account.  This is relevant for cases where the
30756dc79ceSDavid van Moolenbroek 	 * same semaphore is referenced by more than one operation, for example
30856dc79ceSDavid van Moolenbroek 	 * to perform an atomic increase-if-zero action on a single semaphore.
30956dc79ceSDavid van Moolenbroek 	 * As a result, we must optimistically modify semaphore values and roll
31056dc79ceSDavid van Moolenbroek 	 * back on suspension or failure afterwards.
31156dc79ceSDavid van Moolenbroek 	 */
31256dc79ceSDavid van Moolenbroek 	r = OK;
31356dc79ceSDavid van Moolenbroek 	op = NULL;
31456dc79ceSDavid van Moolenbroek 	for (i = 0; i < nsops; i++) {
31556dc79ceSDavid van Moolenbroek 		sp = &sem->sems[sops[i].sem_num];
31656dc79ceSDavid van Moolenbroek 		op = &sops[i];
3174d272e5aSDavid van Moolenbroek 
31856dc79ceSDavid van Moolenbroek 		if (op->sem_op > 0) {
31956dc79ceSDavid van Moolenbroek 			if (SEMVMX - sp->semval < op->sem_op) {
32056dc79ceSDavid van Moolenbroek 				r = ERANGE;
32156dc79ceSDavid van Moolenbroek 				break;
322433d6423SLionel Sambuc 			}
32356dc79ceSDavid van Moolenbroek 			sp->semval += op->sem_op;
32456dc79ceSDavid van Moolenbroek 		} else if (op->sem_op < 0) {
32556dc79ceSDavid van Moolenbroek 			/*
32656dc79ceSDavid van Moolenbroek 			 * No SEMVMX check; if the process wants to deadlock
32756dc79ceSDavid van Moolenbroek 			 * itself by supplying -SEMVMX it is free to do so..
32856dc79ceSDavid van Moolenbroek 			 */
32956dc79ceSDavid van Moolenbroek 			if ((int)sp->semval < -(int)op->sem_op) {
33056dc79ceSDavid van Moolenbroek 				r = (op->sem_flg & IPC_NOWAIT) ? EAGAIN :
33156dc79ceSDavid van Moolenbroek 				    SUSPEND;
33256dc79ceSDavid van Moolenbroek 				break;
333433d6423SLionel Sambuc 			}
33456dc79ceSDavid van Moolenbroek 			sp->semval += op->sem_op;
33556dc79ceSDavid van Moolenbroek 		} else /* (op->sem_op == 0) */ {
33656dc79ceSDavid van Moolenbroek 			if (sp->semval != 0) {
33756dc79ceSDavid van Moolenbroek 				r = (op->sem_flg & IPC_NOWAIT) ? EAGAIN :
33856dc79ceSDavid van Moolenbroek 				    SUSPEND;
33956dc79ceSDavid van Moolenbroek 				break;
340433d6423SLionel Sambuc 			}
341433d6423SLionel Sambuc 		}
342433d6423SLionel Sambuc 	}
343433d6423SLionel Sambuc 
34456dc79ceSDavid van Moolenbroek 	/*
34556dc79ceSDavid van Moolenbroek 	 * If we did not go through all the operations, then either an error
34656dc79ceSDavid van Moolenbroek 	 * occurred or the user process is to be suspended.  In that case we
34756dc79ceSDavid van Moolenbroek 	 * must roll back any progress we have made so far, and return the
34856dc79ceSDavid van Moolenbroek 	 * operation that caused the call to block.
34956dc79ceSDavid van Moolenbroek 	 */
35056dc79ceSDavid van Moolenbroek 	if (i < nsops) {
35156dc79ceSDavid van Moolenbroek 		assert(op != NULL);
35256dc79ceSDavid van Moolenbroek 		*blkop = op;
35356dc79ceSDavid van Moolenbroek 
35456dc79ceSDavid van Moolenbroek 		/* Roll back all changes made so far. */
35556dc79ceSDavid van Moolenbroek 		while (i-- > 0)
35656dc79ceSDavid van Moolenbroek 			sem->sems[sops[i].sem_num].semval -= sops[i].sem_op;
35756dc79ceSDavid van Moolenbroek 
35856dc79ceSDavid van Moolenbroek 		assert(r != OK);
35956dc79ceSDavid van Moolenbroek 		return r;
36056dc79ceSDavid van Moolenbroek 	}
36156dc79ceSDavid van Moolenbroek 
36256dc79ceSDavid van Moolenbroek 	/*
36356dc79ceSDavid van Moolenbroek 	 * The operation has completed successfully.  Also update all affected
36456dc79ceSDavid van Moolenbroek 	 * semaphores' PID values, and the semaphore set's last-semop time.
36556dc79ceSDavid van Moolenbroek 	 * The caller must do everything else.
36656dc79ceSDavid van Moolenbroek 	 */
36756dc79ceSDavid van Moolenbroek 	for (i = 0; i < nsops; i++)
36856dc79ceSDavid van Moolenbroek 		sem->sems[sops[i].sem_num].sempid = pid;
36956dc79ceSDavid van Moolenbroek 
37056dc79ceSDavid van Moolenbroek 	sem->semid_ds.sem_otime = clock_time(NULL);
37156dc79ceSDavid van Moolenbroek 
37256dc79ceSDavid van Moolenbroek 	return OK;
37356dc79ceSDavid van Moolenbroek }
37456dc79ceSDavid van Moolenbroek 
37556dc79ceSDavid van Moolenbroek /*
37656dc79ceSDavid van Moolenbroek  * Check whether any blocked operations can now be satisfied on any of the
37756dc79ceSDavid van Moolenbroek  * semaphores in the given semaphore set.  Do this repeatedly as necessary, as
37856dc79ceSDavid van Moolenbroek  * any unblocked operation may in turn allow other operations to be resumed.
37956dc79ceSDavid van Moolenbroek  */
3800baafa0eSDavid van Moolenbroek static void
check_set(struct sem_struct * sem)38156dc79ceSDavid van Moolenbroek check_set(struct sem_struct * sem)
382433d6423SLionel Sambuc {
38356dc79ceSDavid van Moolenbroek 	struct iproc *ip, *nextip;
38456dc79ceSDavid van Moolenbroek 	struct sembuf *blkop;
38556dc79ceSDavid van Moolenbroek 	int r, woken_up;
386433d6423SLionel Sambuc 
38756dc79ceSDavid van Moolenbroek 	/*
38856dc79ceSDavid van Moolenbroek 	 * Go through all the waiting processes in FIFO order, which is our
38956dc79ceSDavid van Moolenbroek 	 * best attempt at providing at least some fairness.  Keep trying as
39056dc79ceSDavid van Moolenbroek 	 * long as we woke up at least one process, which means we made actual
39156dc79ceSDavid van Moolenbroek 	 * progress.
39256dc79ceSDavid van Moolenbroek 	 */
39356dc79ceSDavid van Moolenbroek 	do {
39456dc79ceSDavid van Moolenbroek 		woken_up = FALSE;
395433d6423SLionel Sambuc 
39656dc79ceSDavid van Moolenbroek 		TAILQ_FOREACH_SAFE(ip, &sem->waiters, ip_next, nextip) {
39756dc79ceSDavid van Moolenbroek 			/* Retry the entire semop(2) operation, atomically. */
39856dc79ceSDavid van Moolenbroek 			r = try_semop(ip->ip_sem, ip->ip_sops, ip->ip_nsops,
39956dc79ceSDavid van Moolenbroek 			    ip->ip_pid, &blkop);
400433d6423SLionel Sambuc 
40156dc79ceSDavid van Moolenbroek 			if (r != SUSPEND) {
40256dc79ceSDavid van Moolenbroek 				/* Success or failure. */
40356dc79ceSDavid van Moolenbroek 				complete_semop(ip, r);
404433d6423SLionel Sambuc 
40556dc79ceSDavid van Moolenbroek 				/* No changes are made on failure. */
40656dc79ceSDavid van Moolenbroek 				if (r == OK)
40756dc79ceSDavid van Moolenbroek 					woken_up = TRUE;
40856dc79ceSDavid van Moolenbroek 			} else if (blkop != ip->ip_blkop) {
40956dc79ceSDavid van Moolenbroek 				/*
41056dc79ceSDavid van Moolenbroek 				 * The process stays suspended, but it is now
41156dc79ceSDavid van Moolenbroek 				 * blocked on a different semaphore.  As a
41256dc79ceSDavid van Moolenbroek 				 * result, we need to adjust the semaphores'
41356dc79ceSDavid van Moolenbroek 				 * suspension counts.
41456dc79ceSDavid van Moolenbroek 				 */
41556dc79ceSDavid van Moolenbroek 				dec_susp_count(ip);
4160baafa0eSDavid van Moolenbroek 
41756dc79ceSDavid van Moolenbroek 				ip->ip_blkop = blkop;
418433d6423SLionel Sambuc 
41956dc79ceSDavid van Moolenbroek 				inc_susp_count(ip);
420433d6423SLionel Sambuc 			}
421433d6423SLionel Sambuc 		}
42256dc79ceSDavid van Moolenbroek 	} while (woken_up);
423433d6423SLionel Sambuc }
424433d6423SLionel Sambuc 
42556dc79ceSDavid van Moolenbroek /*
426*53458494SDavid van Moolenbroek  * Fill a seminfo structure with actual information.  The information returned
427*53458494SDavid van Moolenbroek  * depends on the given command, which may be either IPC_INFO or SEM_INFO.
428*53458494SDavid van Moolenbroek  */
429*53458494SDavid van Moolenbroek static void
fill_seminfo(struct seminfo * sinfo,int cmd)430*53458494SDavid van Moolenbroek fill_seminfo(struct seminfo * sinfo, int cmd)
431*53458494SDavid van Moolenbroek {
432*53458494SDavid van Moolenbroek 	unsigned int i;
433*53458494SDavid van Moolenbroek 
434*53458494SDavid van Moolenbroek 	assert(cmd == IPC_INFO || cmd == SEM_INFO);
435*53458494SDavid van Moolenbroek 
436*53458494SDavid van Moolenbroek 	memset(sinfo, 0, sizeof(*sinfo));
437*53458494SDavid van Moolenbroek 
438*53458494SDavid van Moolenbroek 	sinfo->semmap = SEMMNI;
439*53458494SDavid van Moolenbroek 	sinfo->semmni = SEMMNI;
440*53458494SDavid van Moolenbroek 	sinfo->semmns = SEMMNI * SEMMSL;
441*53458494SDavid van Moolenbroek 	sinfo->semmnu = 0; /* TODO: support for SEM_UNDO */
442*53458494SDavid van Moolenbroek 	sinfo->semmsl = SEMMSL;
443*53458494SDavid van Moolenbroek 	sinfo->semopm = SEMOPM;
444*53458494SDavid van Moolenbroek 	sinfo->semume = 0; /* TODO: support for SEM_UNDO */
445*53458494SDavid van Moolenbroek 	if (cmd == SEM_INFO) {
446*53458494SDavid van Moolenbroek 		/*
447*53458494SDavid van Moolenbroek 		 * For SEM_INFO the semusz field is expected to contain the
448*53458494SDavid van Moolenbroek 		 * number of semaphore sets currently in use.
449*53458494SDavid van Moolenbroek 		 */
450*53458494SDavid van Moolenbroek 		sinfo->semusz = sem_list_nr;
451*53458494SDavid van Moolenbroek 	} else
452*53458494SDavid van Moolenbroek 		sinfo->semusz = 0; /* TODO: support for SEM_UNDO */
453*53458494SDavid van Moolenbroek 	sinfo->semvmx = SEMVMX;
454*53458494SDavid van Moolenbroek 	if (cmd == SEM_INFO) {
455*53458494SDavid van Moolenbroek 		/*
456*53458494SDavid van Moolenbroek 		 * For SEM_INFO the semaem field is expected to contain
457*53458494SDavid van Moolenbroek 		 * the total number of allocated semaphores.
458*53458494SDavid van Moolenbroek 		 */
459*53458494SDavid van Moolenbroek 		for (i = 0; i < sem_list_nr; i++)
460*53458494SDavid van Moolenbroek 			sinfo->semaem += sem_list[i].semid_ds.sem_nsems;
461*53458494SDavid van Moolenbroek 	} else
462*53458494SDavid van Moolenbroek 		sinfo->semaem = 0; /* TODO: support for SEM_UNDO */
463*53458494SDavid van Moolenbroek }
464*53458494SDavid van Moolenbroek 
465*53458494SDavid van Moolenbroek /*
46656dc79ceSDavid van Moolenbroek  * Implementation of the semctl(2) system call.
46756dc79ceSDavid van Moolenbroek  */
4680baafa0eSDavid van Moolenbroek int
do_semctl(message * m)4690baafa0eSDavid van Moolenbroek do_semctl(message * m)
470433d6423SLionel Sambuc {
47156dc79ceSDavid van Moolenbroek 	static unsigned short valbuf[SEMMSL];
4724d272e5aSDavid van Moolenbroek 	unsigned int i;
4734d272e5aSDavid van Moolenbroek 	vir_bytes opt;
474433d6423SLionel Sambuc 	uid_t uid;
4754d272e5aSDavid van Moolenbroek 	int r, id, num, cmd, val;
4760baafa0eSDavid van Moolenbroek 	struct semid_ds tmp_ds;
477433d6423SLionel Sambuc 	struct sem_struct *sem;
47858c1923cSDavid van Moolenbroek 	struct seminfo sinfo;
479433d6423SLionel Sambuc 
480433d6423SLionel Sambuc 	id = m->m_lc_ipc_semctl.id;
481433d6423SLionel Sambuc 	num = m->m_lc_ipc_semctl.num;
482433d6423SLionel Sambuc 	cmd = m->m_lc_ipc_semctl.cmd;
483433d6423SLionel Sambuc 	opt = m->m_lc_ipc_semctl.opt;
484433d6423SLionel Sambuc 
48556dc79ceSDavid van Moolenbroek 	/*
48656dc79ceSDavid van Moolenbroek 	 * Look up the target semaphore set.  The IPC_INFO and SEM_INFO
48756dc79ceSDavid van Moolenbroek 	 * commands have no associated semaphore set.  The SEM_STAT command
48856dc79ceSDavid van Moolenbroek 	 * takes an array index into the semaphore set table.  For all other
48956dc79ceSDavid van Moolenbroek 	 * commands, look up the semaphore set by its given identifier.
49056dc79ceSDavid van Moolenbroek 	 * */
49158c1923cSDavid van Moolenbroek 	switch (cmd) {
49258c1923cSDavid van Moolenbroek 	case IPC_INFO:
49358c1923cSDavid van Moolenbroek 	case SEM_INFO:
49458c1923cSDavid van Moolenbroek 		sem = NULL;
49558c1923cSDavid van Moolenbroek 		break;
49658c1923cSDavid van Moolenbroek 	case SEM_STAT:
4974d272e5aSDavid van Moolenbroek 		if (id < 0 || (unsigned int)id >= sem_list_nr)
49858c1923cSDavid van Moolenbroek 			return EINVAL;
49958c1923cSDavid van Moolenbroek 		sem = &sem_list[id];
5004d272e5aSDavid van Moolenbroek 		if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
5014d272e5aSDavid van Moolenbroek 			return EINVAL;
50258c1923cSDavid van Moolenbroek 		break;
50358c1923cSDavid van Moolenbroek 	default:
5040baafa0eSDavid van Moolenbroek 		if ((sem = sem_find_id(id)) == NULL)
505433d6423SLionel Sambuc 			return EINVAL;
5064d272e5aSDavid van Moolenbroek 		break;
507433d6423SLionel Sambuc 	}
508433d6423SLionel Sambuc 
5090baafa0eSDavid van Moolenbroek 	/*
51056dc79ceSDavid van Moolenbroek 	 * Check if the caller has the appropriate permissions on the target
51156dc79ceSDavid van Moolenbroek 	 * semaphore set.  SETVAL and SETALL require write permission.  IPC_SET
51256dc79ceSDavid van Moolenbroek 	 * and IPC_RMID require ownership permission, and return EPERM instead
51356dc79ceSDavid van Moolenbroek 	 * of EACCES on failure.  IPC_INFO and SEM_INFO are free for general
51456dc79ceSDavid van Moolenbroek 	 * use.  All other calls require read permission.
5150baafa0eSDavid van Moolenbroek 	 */
51656dc79ceSDavid van Moolenbroek 	switch (cmd) {
51756dc79ceSDavid van Moolenbroek 	case SETVAL:
51856dc79ceSDavid van Moolenbroek 	case SETALL:
51956dc79ceSDavid van Moolenbroek 		assert(sem != NULL);
52056dc79ceSDavid van Moolenbroek 		if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, IPC_W))
52156dc79ceSDavid van Moolenbroek 			return EACCES;
52256dc79ceSDavid van Moolenbroek 		break;
52356dc79ceSDavid van Moolenbroek 	case IPC_SET:
52456dc79ceSDavid van Moolenbroek 	case IPC_RMID:
52556dc79ceSDavid van Moolenbroek 		assert(sem != NULL);
52656dc79ceSDavid van Moolenbroek 		uid = getnuid(m->m_source);
52756dc79ceSDavid van Moolenbroek 		if (uid != sem->semid_ds.sem_perm.cuid &&
52856dc79ceSDavid van Moolenbroek 		    uid != sem->semid_ds.sem_perm.uid && uid != 0)
52956dc79ceSDavid van Moolenbroek 			return EPERM;
53056dc79ceSDavid van Moolenbroek 		break;
53156dc79ceSDavid van Moolenbroek 	case IPC_INFO:
53256dc79ceSDavid van Moolenbroek 	case SEM_INFO:
53356dc79ceSDavid van Moolenbroek 		break;
53456dc79ceSDavid van Moolenbroek 	default:
53556dc79ceSDavid van Moolenbroek 		assert(sem != NULL);
53656dc79ceSDavid van Moolenbroek 		if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, IPC_R))
537433d6423SLionel Sambuc 			return EACCES;
538433d6423SLionel Sambuc 	}
539433d6423SLionel Sambuc 
540433d6423SLionel Sambuc 	switch (cmd) {
541433d6423SLionel Sambuc 	case IPC_STAT:
54258c1923cSDavid van Moolenbroek 	case SEM_STAT:
5430baafa0eSDavid van Moolenbroek 		if ((r = sys_datacopy(SELF, (vir_bytes)&sem->semid_ds,
5440baafa0eSDavid van Moolenbroek 		    m->m_source, opt, sizeof(sem->semid_ds))) != OK)
54558c1923cSDavid van Moolenbroek 			return r;
5464d272e5aSDavid van Moolenbroek 		if (cmd == SEM_STAT)
5474d272e5aSDavid van Moolenbroek 			m->m_lc_ipc_semctl.ret =
5484d272e5aSDavid van Moolenbroek 			    IXSEQ_TO_IPCID(id, sem->semid_ds.sem_perm);
549433d6423SLionel Sambuc 		break;
550433d6423SLionel Sambuc 	case IPC_SET:
5510baafa0eSDavid van Moolenbroek 		if ((r = sys_datacopy(m->m_source, opt, SELF,
5520baafa0eSDavid van Moolenbroek 		    (vir_bytes)&tmp_ds, sizeof(tmp_ds))) != OK)
5534d272e5aSDavid van Moolenbroek 			return r;
554433d6423SLionel Sambuc 		sem->semid_ds.sem_perm.uid = tmp_ds.sem_perm.uid;
555433d6423SLionel Sambuc 		sem->semid_ds.sem_perm.gid = tmp_ds.sem_perm.gid;
5564d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_perm.mode &= ~ACCESSPERMS;
5574d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_perm.mode |=
5584d272e5aSDavid van Moolenbroek 		    tmp_ds.sem_perm.mode & ACCESSPERMS;
5594d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_ctime = clock_time(NULL);
560433d6423SLionel Sambuc 		break;
561433d6423SLionel Sambuc 	case IPC_RMID:
5620baafa0eSDavid van Moolenbroek 		/*
5630baafa0eSDavid van Moolenbroek 		 * Awaken all processes blocked in semop(2) on any semaphore in
5640baafa0eSDavid van Moolenbroek 		 * this set, and remove the semaphore set itself.
565433d6423SLionel Sambuc 		 */
56656dc79ceSDavid van Moolenbroek 		remove_set(sem);
567433d6423SLionel Sambuc 		break;
568433d6423SLionel Sambuc 	case IPC_INFO:
569433d6423SLionel Sambuc 	case SEM_INFO:
570*53458494SDavid van Moolenbroek 		fill_seminfo(&sinfo, cmd);
57158c1923cSDavid van Moolenbroek 
5720baafa0eSDavid van Moolenbroek 		if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source,
5730baafa0eSDavid van Moolenbroek 		    opt, sizeof(sinfo))) != OK)
57458c1923cSDavid van Moolenbroek 			return r;
57558c1923cSDavid van Moolenbroek 		/* Return the highest in-use slot number if any, or zero. */
57658c1923cSDavid van Moolenbroek 		if (sem_list_nr > 0)
57758c1923cSDavid van Moolenbroek 			m->m_lc_ipc_semctl.ret = sem_list_nr - 1;
57858c1923cSDavid van Moolenbroek 		else
57958c1923cSDavid van Moolenbroek 			m->m_lc_ipc_semctl.ret = 0;
580433d6423SLionel Sambuc 		break;
581433d6423SLionel Sambuc 	case GETALL:
58256dc79ceSDavid van Moolenbroek 		assert(sem->semid_ds.sem_nsems <= __arraycount(valbuf));
583433d6423SLionel Sambuc 		for (i = 0; i < sem->semid_ds.sem_nsems; i++)
58456dc79ceSDavid van Moolenbroek 			valbuf[i] = sem->sems[i].semval;
58556dc79ceSDavid van Moolenbroek 		r = sys_datacopy(SELF, (vir_bytes)valbuf, m->m_source,
5860baafa0eSDavid van Moolenbroek 		    opt, sizeof(unsigned short) * sem->semid_ds.sem_nsems);
587433d6423SLionel Sambuc 		if (r != OK)
58856dc79ceSDavid van Moolenbroek 			return r;
589433d6423SLionel Sambuc 		break;
590433d6423SLionel Sambuc 	case GETNCNT:
591433d6423SLionel Sambuc 		if (num < 0 || num >= sem->semid_ds.sem_nsems)
592433d6423SLionel Sambuc 			return EINVAL;
593433d6423SLionel Sambuc 		m->m_lc_ipc_semctl.ret = sem->sems[num].semncnt;
594433d6423SLionel Sambuc 		break;
595433d6423SLionel Sambuc 	case GETPID:
596433d6423SLionel Sambuc 		if (num < 0 || num >= sem->semid_ds.sem_nsems)
597433d6423SLionel Sambuc 			return EINVAL;
598433d6423SLionel Sambuc 		m->m_lc_ipc_semctl.ret = sem->sems[num].sempid;
599433d6423SLionel Sambuc 		break;
600433d6423SLionel Sambuc 	case GETVAL:
601433d6423SLionel Sambuc 		if (num < 0 || num >= sem->semid_ds.sem_nsems)
602433d6423SLionel Sambuc 			return EINVAL;
603433d6423SLionel Sambuc 		m->m_lc_ipc_semctl.ret = sem->sems[num].semval;
604433d6423SLionel Sambuc 		break;
605433d6423SLionel Sambuc 	case GETZCNT:
606433d6423SLionel Sambuc 		if (num < 0 || num >= sem->semid_ds.sem_nsems)
607433d6423SLionel Sambuc 			return EINVAL;
608433d6423SLionel Sambuc 		m->m_lc_ipc_semctl.ret = sem->sems[num].semzcnt;
609433d6423SLionel Sambuc 		break;
610433d6423SLionel Sambuc 	case SETALL:
61156dc79ceSDavid van Moolenbroek 		assert(sem->semid_ds.sem_nsems <= __arraycount(valbuf));
61256dc79ceSDavid van Moolenbroek 		r = sys_datacopy(m->m_source, opt, SELF, (vir_bytes)valbuf,
613433d6423SLionel Sambuc 		    sizeof(unsigned short) * sem->semid_ds.sem_nsems);
61456dc79ceSDavid van Moolenbroek 		if (r != OK)
61556dc79ceSDavid van Moolenbroek 			return r;
61656dc79ceSDavid van Moolenbroek 		for (i = 0; i < sem->semid_ds.sem_nsems; i++)
61756dc79ceSDavid van Moolenbroek 			if (valbuf[i] > SEMVMX)
618433d6423SLionel Sambuc 				return ERANGE;
6190baafa0eSDavid van Moolenbroek #ifdef DEBUG_SEM
6200baafa0eSDavid van Moolenbroek 		for (i = 0; i < sem->semid_ds.sem_nsems; i++)
62156dc79ceSDavid van Moolenbroek 			printf("SEMCTL: SETALL val: [%d] %d\n", i, valbuf[i]);
6220baafa0eSDavid van Moolenbroek #endif
6230baafa0eSDavid van Moolenbroek 		for (i = 0; i < sem->semid_ds.sem_nsems; i++)
62456dc79ceSDavid van Moolenbroek 			sem->sems[i].semval = valbuf[i];
62556dc79ceSDavid van Moolenbroek 		sem->semid_ds.sem_ctime = clock_time(NULL);
6260baafa0eSDavid van Moolenbroek 		/* Awaken any waiting parties if now possible. */
62756dc79ceSDavid van Moolenbroek 		check_set(sem);
628433d6423SLionel Sambuc 		break;
629433d6423SLionel Sambuc 	case SETVAL:
630433d6423SLionel Sambuc 		val = (int)opt;
631433d6423SLionel Sambuc 		if (num < 0 || num >= sem->semid_ds.sem_nsems)
632433d6423SLionel Sambuc 			return EINVAL;
633433d6423SLionel Sambuc 		if (val < 0 || val > SEMVMX)
634433d6423SLionel Sambuc 			return ERANGE;
635433d6423SLionel Sambuc 		sem->sems[num].semval = val;
636433d6423SLionel Sambuc #ifdef DEBUG_SEM
637433d6423SLionel Sambuc 		printf("SEMCTL: SETVAL: %d %d\n", num, val);
638433d6423SLionel Sambuc #endif
6394d272e5aSDavid van Moolenbroek 		sem->semid_ds.sem_ctime = clock_time(NULL);
6400baafa0eSDavid van Moolenbroek 		/* Awaken any waiting parties if now possible. */
64156dc79ceSDavid van Moolenbroek 		check_set(sem);
642433d6423SLionel Sambuc 		break;
643433d6423SLionel Sambuc 	default:
644433d6423SLionel Sambuc 		return EINVAL;
645433d6423SLionel Sambuc 	}
646433d6423SLionel Sambuc 
647433d6423SLionel Sambuc 	return OK;
648433d6423SLionel Sambuc }
649433d6423SLionel Sambuc 
65056dc79ceSDavid van Moolenbroek /*
65156dc79ceSDavid van Moolenbroek  * Implementation of the semop(2) system call.
65256dc79ceSDavid van Moolenbroek  */
6530baafa0eSDavid van Moolenbroek int
do_semop(message * m)6540baafa0eSDavid van Moolenbroek do_semop(message * m)
655433d6423SLionel Sambuc {
65656dc79ceSDavid van Moolenbroek 	unsigned int i, mask, slot;
6574d272e5aSDavid van Moolenbroek 	int id, r;
65856dc79ceSDavid van Moolenbroek 	struct sembuf *sops, *blkop;
659433d6423SLionel Sambuc 	unsigned int nsops;
660433d6423SLionel Sambuc 	struct sem_struct *sem;
66156dc79ceSDavid van Moolenbroek 	struct iproc *ip;
66256dc79ceSDavid van Moolenbroek 	pid_t pid;
663433d6423SLionel Sambuc 
664433d6423SLionel Sambuc 	id = m->m_lc_ipc_semop.id;
665433d6423SLionel Sambuc 	nsops = m->m_lc_ipc_semop.size;
666433d6423SLionel Sambuc 
6670baafa0eSDavid van Moolenbroek 	if ((sem = sem_find_id(id)) == NULL)
6684d272e5aSDavid van Moolenbroek 		return EINVAL;
669433d6423SLionel Sambuc 
67056dc79ceSDavid van Moolenbroek 	if (nsops == 0)
67156dc79ceSDavid van Moolenbroek 		return OK; /* nothing to do */
672433d6423SLionel Sambuc 	if (nsops > SEMOPM)
6734d272e5aSDavid van Moolenbroek 		return E2BIG;
674433d6423SLionel Sambuc 
6750baafa0eSDavid van Moolenbroek 	/* Get the array from the user process. */
6760baafa0eSDavid van Moolenbroek 	sops = malloc(sizeof(sops[0]) * nsops);
67756dc79ceSDavid van Moolenbroek 	if (sops == NULL)
6784d272e5aSDavid van Moolenbroek 		return ENOMEM;
6790baafa0eSDavid van Moolenbroek 	r = sys_datacopy(m->m_source, (vir_bytes)m->m_lc_ipc_semop.ops, SELF,
6800baafa0eSDavid van Moolenbroek 	    (vir_bytes)sops, sizeof(sops[0]) * nsops);
6814d272e5aSDavid van Moolenbroek 	if (r != OK)
682433d6423SLionel Sambuc 		goto out_free;
683433d6423SLionel Sambuc 
684433d6423SLionel Sambuc #ifdef DEBUG_SEM
685433d6423SLionel Sambuc 	for (i = 0; i < nsops; i++)
686433d6423SLionel Sambuc 		printf("SEMOP: num:%d  op:%d  flg:%d\n",
687433d6423SLionel Sambuc 			sops[i].sem_num, sops[i].sem_op, sops[i].sem_flg);
688433d6423SLionel Sambuc #endif
68956dc79ceSDavid van Moolenbroek 	/*
69056dc79ceSDavid van Moolenbroek 	 * Check for permissions.  We do this only once, even though the call
69156dc79ceSDavid van Moolenbroek 	 * might suspend and the semaphore set's permissions might be changed
69256dc79ceSDavid van Moolenbroek 	 * before the call resumes.  The specification is not clear on this.
69356dc79ceSDavid van Moolenbroek 	 * Either way, perform the permission check before checking on the
69456dc79ceSDavid van Moolenbroek 	 * validity of semaphore numbers, since obtaining the semaphore set
69556dc79ceSDavid van Moolenbroek 	 * size itself requires read permission (except through sysctl(2)..).
69656dc79ceSDavid van Moolenbroek 	 */
69756dc79ceSDavid van Moolenbroek 	mask = 0;
69856dc79ceSDavid van Moolenbroek 	for (i = 0; i < nsops; i++) {
69956dc79ceSDavid van Moolenbroek 		if (sops[i].sem_op != 0)
70056dc79ceSDavid van Moolenbroek 			mask |= IPC_W; /* check for write permission */
70156dc79ceSDavid van Moolenbroek 		else
70256dc79ceSDavid van Moolenbroek 			mask |= IPC_R; /* check for read permission */
70356dc79ceSDavid van Moolenbroek 	}
70456dc79ceSDavid van Moolenbroek 	r = EACCES;
70556dc79ceSDavid van Moolenbroek 	if (!check_perm(&sem->semid_ds.sem_perm, m->m_source, mask))
70656dc79ceSDavid van Moolenbroek 		goto out_free;
70756dc79ceSDavid van Moolenbroek 
7080baafa0eSDavid van Moolenbroek 	/* Check that all given semaphore numbers are within range. */
709433d6423SLionel Sambuc 	r = EFBIG;
710433d6423SLionel Sambuc 	for (i = 0; i < nsops; i++)
711433d6423SLionel Sambuc 		if (sops[i].sem_num >= sem->semid_ds.sem_nsems)
712433d6423SLionel Sambuc 			goto out_free;
713433d6423SLionel Sambuc 
71456dc79ceSDavid van Moolenbroek 	/*
71556dc79ceSDavid van Moolenbroek 	 * Do not check if the same semaphore is referenced more than once
71656dc79ceSDavid van Moolenbroek 	 * (there was such a check here originally), because that is actually
71756dc79ceSDavid van Moolenbroek 	 * a valid case.  The result is however that it is possible to
71856dc79ceSDavid van Moolenbroek 	 * construct a semop(2) request that will never complete, and thus,
71956dc79ceSDavid van Moolenbroek 	 * care must be taken that such requests do not create potential
72056dc79ceSDavid van Moolenbroek 	 * deadlock situations etc.
72156dc79ceSDavid van Moolenbroek 	 */
7224d272e5aSDavid van Moolenbroek 
72356dc79ceSDavid van Moolenbroek 	pid = getnpid(m->m_source);
724433d6423SLionel Sambuc 
72556dc79ceSDavid van Moolenbroek 	/*
72656dc79ceSDavid van Moolenbroek 	 * We do not yet support SEM_UNDO at all, so we better not give the
72756dc79ceSDavid van Moolenbroek 	 * caller the impression that we do.  For now, print a warning so that
72856dc79ceSDavid van Moolenbroek 	 * we know when an application actually fails for that reason.
72956dc79ceSDavid van Moolenbroek 	 */
73056dc79ceSDavid van Moolenbroek 	for (i = 0; i < nsops; i++) {
73156dc79ceSDavid van Moolenbroek 		if (sops[i].sem_flg & SEM_UNDO) {
73256dc79ceSDavid van Moolenbroek 			/* Print a warning only if this isn't the test set.. */
73356dc79ceSDavid van Moolenbroek 			if (sops[i].sem_flg != SHRT_MAX)
73456dc79ceSDavid van Moolenbroek 				printf("IPC: pid %d tried to use SEM_UNDO\n",
73556dc79ceSDavid van Moolenbroek 				    pid);
73656dc79ceSDavid van Moolenbroek 			r = EINVAL;
737433d6423SLionel Sambuc 			goto out_free;
738433d6423SLionel Sambuc 		}
739433d6423SLionel Sambuc 	}
740433d6423SLionel Sambuc 
74156dc79ceSDavid van Moolenbroek 	/* Try to perform the operation now. */
74256dc79ceSDavid van Moolenbroek 	r = try_semop(sem, sops, nsops, pid, &blkop);
7430baafa0eSDavid van Moolenbroek 
74456dc79ceSDavid van Moolenbroek 	if (r == SUSPEND) {
74556dc79ceSDavid van Moolenbroek 		/*
74656dc79ceSDavid van Moolenbroek 		 * The operation ended up blocking on a particular semaphore
74756dc79ceSDavid van Moolenbroek 		 * operation.  Save all details in the slot for the user
74856dc79ceSDavid van Moolenbroek 		 * process, and add it to the list of processes waiting for
74956dc79ceSDavid van Moolenbroek 		 * this semaphore set.
75056dc79ceSDavid van Moolenbroek 		 */
75156dc79ceSDavid van Moolenbroek 		slot = _ENDPOINT_P(m->m_source);
75256dc79ceSDavid van Moolenbroek 		assert(slot < __arraycount(iproc));
7530baafa0eSDavid van Moolenbroek 
75456dc79ceSDavid van Moolenbroek 		ip = &iproc[slot];
75556dc79ceSDavid van Moolenbroek 		assert(ip->ip_sem == NULL); /* can't already be in use */
75656dc79ceSDavid van Moolenbroek 
75756dc79ceSDavid van Moolenbroek 		ip->ip_endpt = m->m_source;
75856dc79ceSDavid van Moolenbroek 		ip->ip_pid = pid;
75956dc79ceSDavid van Moolenbroek 		ip->ip_sem = sem;
76056dc79ceSDavid van Moolenbroek 		ip->ip_sops = sops;
76156dc79ceSDavid van Moolenbroek 		ip->ip_nsops = nsops;
76256dc79ceSDavid van Moolenbroek 		ip->ip_blkop = blkop;
76356dc79ceSDavid van Moolenbroek 
76456dc79ceSDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&sem->waiters, ip, ip_next);
76556dc79ceSDavid van Moolenbroek 
76656dc79ceSDavid van Moolenbroek 		inc_susp_count(ip);
767433d6423SLionel Sambuc 
7684d272e5aSDavid van Moolenbroek 		return r;
769433d6423SLionel Sambuc 	}
770433d6423SLionel Sambuc 
77156dc79ceSDavid van Moolenbroek out_free:
77256dc79ceSDavid van Moolenbroek 	free(sops);
77356dc79ceSDavid van Moolenbroek 
77456dc79ceSDavid van Moolenbroek 	/* Awaken any other waiting parties if now possible. */
77556dc79ceSDavid van Moolenbroek 	if (r == OK)
77656dc79ceSDavid van Moolenbroek 		check_set(sem);
77756dc79ceSDavid van Moolenbroek 
77856dc79ceSDavid van Moolenbroek 	return r;
77956dc79ceSDavid van Moolenbroek }
78056dc79ceSDavid van Moolenbroek 
78156dc79ceSDavid van Moolenbroek /*
782*53458494SDavid van Moolenbroek  * Return semaphore information for a remote MIB call on the sysvipc_info node
783*53458494SDavid van Moolenbroek  * in the kern.ipc subtree.  The particular semantics of this call are tightly
784*53458494SDavid van Moolenbroek  * coupled to the implementation of the ipcs(1) userland utility.
785*53458494SDavid van Moolenbroek  */
786*53458494SDavid van Moolenbroek ssize_t
get_sem_mib_info(struct rmib_oldp * oldp)787*53458494SDavid van Moolenbroek get_sem_mib_info(struct rmib_oldp * oldp)
788*53458494SDavid van Moolenbroek {
789*53458494SDavid van Moolenbroek 	struct sem_sysctl_info semsi;
790*53458494SDavid van Moolenbroek 	struct semid_ds *semds;
791*53458494SDavid van Moolenbroek 	unsigned int i;
792*53458494SDavid van Moolenbroek 	ssize_t r, off;
793*53458494SDavid van Moolenbroek 
794*53458494SDavid van Moolenbroek 	off = 0;
795*53458494SDavid van Moolenbroek 
796*53458494SDavid van Moolenbroek 	fill_seminfo(&semsi.seminfo, IPC_INFO);
797*53458494SDavid van Moolenbroek 
798*53458494SDavid van Moolenbroek 	/*
799*53458494SDavid van Moolenbroek 	 * As a hackish exception, the requested size may imply that just
800*53458494SDavid van Moolenbroek 	 * general information is to be returned, without throwing an ENOMEM
801*53458494SDavid van Moolenbroek 	 * error because there is no space for full output.
802*53458494SDavid van Moolenbroek 	 */
803*53458494SDavid van Moolenbroek 	if (rmib_getoldlen(oldp) == sizeof(semsi.seminfo))
804*53458494SDavid van Moolenbroek 		return rmib_copyout(oldp, 0, &semsi.seminfo,
805*53458494SDavid van Moolenbroek 		    sizeof(semsi.seminfo));
806*53458494SDavid van Moolenbroek 
807*53458494SDavid van Moolenbroek 	/*
808*53458494SDavid van Moolenbroek 	 * ipcs(1) blindly expects the returned array to be of size
809*53458494SDavid van Moolenbroek 	 * seminfo.semmni, using the SEM_ALLOC mode flag to see whether each
810*53458494SDavid van Moolenbroek 	 * entry is valid.  If we return a smaller size, ipcs(1) will access
811*53458494SDavid van Moolenbroek 	 * arbitrary memory.
812*53458494SDavid van Moolenbroek 	 */
813*53458494SDavid van Moolenbroek 	assert(semsi.seminfo.semmni > 0);
814*53458494SDavid van Moolenbroek 
815*53458494SDavid van Moolenbroek 	if (oldp == NULL)
816*53458494SDavid van Moolenbroek 		return sizeof(semsi) + sizeof(semsi.semids[0]) *
817*53458494SDavid van Moolenbroek 		    (semsi.seminfo.semmni - 1);
818*53458494SDavid van Moolenbroek 
819*53458494SDavid van Moolenbroek 	/*
820*53458494SDavid van Moolenbroek 	 * Copy out entries one by one.  For the first entry, copy out the
821*53458494SDavid van Moolenbroek 	 * entire "semsi" structure.  For subsequent entries, reuse the single
822*53458494SDavid van Moolenbroek 	 * embedded 'semids' element of "semsi" and copy out only that element.
823*53458494SDavid van Moolenbroek 	 */
824*53458494SDavid van Moolenbroek 	for (i = 0; i < (unsigned int)semsi.seminfo.semmni; i++) {
825*53458494SDavid van Moolenbroek 		semds = &sem_list[i].semid_ds;
826*53458494SDavid van Moolenbroek 
827*53458494SDavid van Moolenbroek 		memset(&semsi.semids[0], 0, sizeof(semsi.semids[0]));
828*53458494SDavid van Moolenbroek 		if (i < sem_list_nr && (semds->sem_perm.mode & SEM_ALLOC)) {
829*53458494SDavid van Moolenbroek 			prepare_mib_perm(&semsi.semids[0].sem_perm,
830*53458494SDavid van Moolenbroek 			    &semds->sem_perm);
831*53458494SDavid van Moolenbroek 			semsi.semids[0].sem_nsems = semds->sem_nsems;
832*53458494SDavid van Moolenbroek 			semsi.semids[0].sem_otime = semds->sem_otime;
833*53458494SDavid van Moolenbroek 			semsi.semids[0].sem_ctime = semds->sem_ctime;
834*53458494SDavid van Moolenbroek 		}
835*53458494SDavid van Moolenbroek 
836*53458494SDavid van Moolenbroek 		if (off == 0)
837*53458494SDavid van Moolenbroek 			r = rmib_copyout(oldp, off, &semsi, sizeof(semsi));
838*53458494SDavid van Moolenbroek 		else
839*53458494SDavid van Moolenbroek 			r = rmib_copyout(oldp, off, &semsi.semids[0],
840*53458494SDavid van Moolenbroek 			    sizeof(semsi.semids[0]));
841*53458494SDavid van Moolenbroek 
842*53458494SDavid van Moolenbroek 		if (r < 0)
843*53458494SDavid van Moolenbroek 			return r;
844*53458494SDavid van Moolenbroek 		off += r;
845*53458494SDavid van Moolenbroek 	}
846*53458494SDavid van Moolenbroek 
847*53458494SDavid van Moolenbroek 	return off;
848*53458494SDavid van Moolenbroek }
849*53458494SDavid van Moolenbroek 
850*53458494SDavid van Moolenbroek /*
85156dc79ceSDavid van Moolenbroek  * Return TRUE iff no semaphore sets are allocated.
85256dc79ceSDavid van Moolenbroek  */
8530baafa0eSDavid van Moolenbroek int
is_sem_nil(void)8540baafa0eSDavid van Moolenbroek is_sem_nil(void)
855433d6423SLionel Sambuc {
8560baafa0eSDavid van Moolenbroek 
857433d6423SLionel Sambuc 	return (sem_list_nr == 0);
858433d6423SLionel Sambuc }
859433d6423SLionel Sambuc 
860910831cbSDavid van Moolenbroek /*
86156dc79ceSDavid van Moolenbroek  * Check if the given endpoint is blocked on a semop(2) call.  If so, cancel
86256dc79ceSDavid van Moolenbroek  * the call, because either it is interrupted by a signal or the process was
86356dc79ceSDavid van Moolenbroek  * killed.  In the former case, unblock the process by replying with EINTR.
864910831cbSDavid van Moolenbroek  */
8650baafa0eSDavid van Moolenbroek void
sem_process_event(endpoint_t endpt,int has_exited)86656dc79ceSDavid van Moolenbroek sem_process_event(endpoint_t endpt, int has_exited)
867433d6423SLionel Sambuc {
86856dc79ceSDavid van Moolenbroek 	unsigned int slot;
86956dc79ceSDavid van Moolenbroek 	struct iproc *ip;
87056dc79ceSDavid van Moolenbroek 
87156dc79ceSDavid van Moolenbroek 	slot = _ENDPOINT_P(endpt);
87256dc79ceSDavid van Moolenbroek 	assert(slot < __arraycount(iproc));
87356dc79ceSDavid van Moolenbroek 
87456dc79ceSDavid van Moolenbroek 	ip = &iproc[slot];
87556dc79ceSDavid van Moolenbroek 
87656dc79ceSDavid van Moolenbroek 	/* Was the process blocked on a semop(2) call at all? */
87756dc79ceSDavid van Moolenbroek 	if (ip->ip_sem == NULL)
87856dc79ceSDavid van Moolenbroek 		return;
87956dc79ceSDavid van Moolenbroek 
88056dc79ceSDavid van Moolenbroek 	assert(ip->ip_endpt == endpt);
881433d6423SLionel Sambuc 
882910831cbSDavid van Moolenbroek 	/*
88356dc79ceSDavid van Moolenbroek 	 * It was; cancel the semop(2) call.  If the process is being removed
88456dc79ceSDavid van Moolenbroek 	 * because its call was interrupted by a signal, then we must wake it
88556dc79ceSDavid van Moolenbroek 	 * up with EINTR.
886910831cbSDavid van Moolenbroek 	 */
88756dc79ceSDavid van Moolenbroek 	complete_semop(ip, has_exited ? EDONTREPLY : EINTR);
888433d6423SLionel Sambuc }
889