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