xref: /openbsd-src/sys/kern/sysv_sem.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*	$OpenBSD: sysv_sem.c,v 1.59 2020/07/08 21:05:42 deraadt Exp $	*/
2 /*	$NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 2002,2003 Todd C. Miller <millert@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23 /*
24  * Implementation of SVID semaphores
25  *
26  * Author:  Daniel Boulet
27  *
28  * This software is provided ``AS IS'' without any warranties of any kind.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/proc.h>
34 #include <sys/sem.h>
35 #include <sys/sysctl.h>
36 #include <sys/malloc.h>
37 #include <sys/pool.h>
38 
39 #include <sys/mount.h>
40 #include <sys/syscallargs.h>
41 
42 #ifdef SEM_DEBUG
43 #define DPRINTF(x)	printf x
44 #else
45 #define DPRINTF(x)
46 #endif
47 
48 int	semtot = 0;
49 int	semutot = 0;
50 struct	semid_ds **sema;	/* semaphore id list */
51 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
52 struct	pool sema_pool;		/* pool for struct semid_ds */
53 struct	pool semu_pool;		/* pool for struct sem_undo (SEMUSZ) */
54 unsigned short *semseqs;	/* array of sem sequence numbers */
55 
56 struct sem_undo *semu_alloc(struct process *);
57 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
58 void semundo_clear(int, int);
59 
60 void
61 seminit(void)
62 {
63 
64 	pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
65 	    "semapl", NULL);
66 	pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
67 	sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
68 	    M_SEM, M_WAITOK|M_ZERO);
69 	semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
70 	    M_SEM, M_WAITOK|M_ZERO);
71 	SLIST_INIT(&semu_list);
72 }
73 
74 /*
75  * Allocate a new sem_undo structure for a process
76  * (returns ptr to structure or NULL if no more room)
77  */
78 struct sem_undo *
79 semu_alloc(struct process *pr)
80 {
81 	struct sem_undo *suptr, *sutmp;
82 
83 	if (semutot == seminfo.semmnu)
84 		return (NULL);		/* no space */
85 
86 	/*
87 	 * Allocate a semu w/o waiting if possible.
88 	 * If we do have to wait, we must check to verify that a semu
89 	 * with un_proc == pr has not been allocated in the meantime.
90 	 */
91 	semutot++;
92 	if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) {
93 		sutmp = pool_get(&semu_pool, PR_WAITOK);
94 		SLIST_FOREACH(suptr, &semu_list, un_next) {
95 			if (suptr->un_proc == pr) {
96 				pool_put(&semu_pool, sutmp);
97 				semutot--;
98 				return (suptr);
99 			}
100 		}
101 		suptr = sutmp;
102 	}
103 	suptr->un_cnt = 0;
104 	suptr->un_proc = pr;
105 	SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
106 	return (suptr);
107 }
108 
109 /*
110  * Adjust a particular entry for a particular proc
111  */
112 int
113 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
114 	int adjval)
115 {
116 	struct process *pr = p->p_p;
117 	struct sem_undo *suptr;
118 	struct undo *sunptr;
119 	int i;
120 
121 	/*
122 	 * Look for and remember the sem_undo if the caller doesn't provide it.
123 	 */
124 	suptr = *supptr;
125 	if (suptr == NULL) {
126 		SLIST_FOREACH(suptr, &semu_list, un_next) {
127 			if (suptr->un_proc == pr) {
128 				*supptr = suptr;
129 				break;
130 			}
131 		}
132 		if (suptr == NULL) {
133 			if (adjval == 0)
134 				return (0);
135 			suptr = semu_alloc(p->p_p);
136 			if (suptr == NULL)
137 				return (ENOSPC);
138 			*supptr = suptr;
139 		}
140 	}
141 
142 	/*
143 	 * Look for the requested entry and adjust it
144 	 * (delete if adjval becomes 0).
145 	 */
146 	sunptr = &suptr->un_ent[0];
147 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
148 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
149 			continue;
150 		if (adjval == 0)
151 			sunptr->un_adjval = 0;
152 		else
153 			sunptr->un_adjval += adjval;
154 		if (sunptr->un_adjval != 0)
155 			return (0);
156 
157 		if (--suptr->un_cnt == 0) {
158 			*supptr = NULL;
159 			SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
160 			pool_put(&semu_pool, suptr);
161 			semutot--;
162 		} else if (i < suptr->un_cnt)
163 			suptr->un_ent[i] =
164 			    suptr->un_ent[suptr->un_cnt];
165 		return (0);
166 	}
167 
168 	/* Didn't find the right entry - create it */
169 	if (adjval == 0)
170 		return (0);
171 	if (suptr->un_cnt == SEMUME)
172 		return (EINVAL);
173 
174 	sunptr = &suptr->un_ent[suptr->un_cnt];
175 	suptr->un_cnt++;
176 	sunptr->un_adjval = adjval;
177 	sunptr->un_id = semid;
178 	sunptr->un_num = semnum;
179 	return (0);
180 }
181 
182 void
183 semundo_clear(int semid, int semnum)
184 {
185 	struct sem_undo *suptr = SLIST_FIRST(&semu_list);
186 	struct sem_undo *suprev = NULL;
187 	struct undo *sunptr;
188 	int i;
189 
190 	while (suptr != NULL) {
191 		sunptr = &suptr->un_ent[0];
192 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
193 			if (sunptr->un_id == semid) {
194 				if (semnum == -1 || sunptr->un_num == semnum) {
195 					suptr->un_cnt--;
196 					if (i < suptr->un_cnt) {
197 						suptr->un_ent[i] =
198 						  suptr->un_ent[suptr->un_cnt];
199 						i--, sunptr--;
200 					}
201 				}
202 				if (semnum != -1)
203 					break;
204 			}
205 		}
206 		if (suptr->un_cnt == 0) {
207 			struct sem_undo *sutmp = suptr;
208 
209 			if (suptr == SLIST_FIRST(&semu_list))
210 				SLIST_REMOVE_HEAD(&semu_list, un_next);
211 			else
212 				SLIST_REMOVE_AFTER(suprev, un_next);
213 			suptr = SLIST_NEXT(suptr, un_next);
214 			pool_put(&semu_pool, sutmp);
215 			semutot--;
216 		} else {
217 			suprev = suptr;
218 			suptr = SLIST_NEXT(suptr, un_next);
219 		}
220 	}
221 }
222 
223 int
224 sys___semctl(struct proc *p, void *v, register_t *retval)
225 {
226 	struct sys___semctl_args /* {
227 		syscallarg(int) semid;
228 		syscallarg(int) semnum;
229 		syscallarg(int) cmd;
230 		syscallarg(union semun *) arg;
231 	} */ *uap = v;
232 	union semun arg;
233 	int error = 0, cmd = SCARG(uap, cmd);
234 
235 	switch (cmd) {
236 	case IPC_SET:
237 	case IPC_STAT:
238 	case GETALL:
239 	case SETVAL:
240 	case SETALL:
241 		error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
242 		break;
243 	}
244 	if (error == 0) {
245 		error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
246 		    cmd, &arg, retval, copyin, copyout);
247 	}
248 	return (error);
249 }
250 
251 int
252 semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
253     register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
254     int (*ds_copyout)(const void *, void *, size_t))
255 {
256 	struct ucred *cred = p->p_ucred;
257 	int i, ix, error = 0;
258 	struct semid_ds sbuf;
259 	struct semid_ds *semaptr;
260 	unsigned short *semval = NULL;
261 
262 	DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
263 
264 	ix = IPCID_TO_IX(semid);
265 	if (ix < 0 || ix >= seminfo.semmni)
266 		return (EINVAL);
267 
268 	if ((semaptr = sema[ix]) == NULL ||
269 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
270 		return (EINVAL);
271 
272 	switch (cmd) {
273 	case IPC_RMID:
274 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
275 			return (error);
276 		semaptr->sem_perm.cuid = cred->cr_uid;
277 		semaptr->sem_perm.uid = cred->cr_uid;
278 		semtot -= semaptr->sem_nsems;
279 		free(semaptr->sem_base, M_SEM,
280 		    semaptr->sem_nsems * sizeof(struct sem));
281 		pool_put(&sema_pool, semaptr);
282 		sema[ix] = NULL;
283 		semundo_clear(ix, -1);
284 		wakeup(&sema[ix]);
285 		break;
286 
287 	case IPC_SET:
288 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
289 			return (error);
290 		if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
291 			return (error);
292 		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
293 		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
294 		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
295 		    (sbuf.sem_perm.mode & 0777);
296 		semaptr->sem_ctime = gettime();
297 		break;
298 
299 	case IPC_STAT:
300 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
301 			return (error);
302 		memcpy(&sbuf, semaptr, sizeof sbuf);
303 		sbuf.sem_base = NULL;
304 		error = ds_copyout(&sbuf, arg->buf, sizeof(struct semid_ds));
305 		break;
306 
307 	case GETNCNT:
308 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
309 			return (error);
310 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
311 			return (EINVAL);
312 		*retval = semaptr->sem_base[semnum].semncnt;
313 		break;
314 
315 	case GETPID:
316 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
317 			return (error);
318 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
319 			return (EINVAL);
320 		*retval = semaptr->sem_base[semnum].sempid;
321 		break;
322 
323 	case GETVAL:
324 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
325 			return (error);
326 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
327 			return (EINVAL);
328 		*retval = semaptr->sem_base[semnum].semval;
329 		break;
330 
331 	case GETALL:
332 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
333 			return (error);
334 		for (i = 0; i < semaptr->sem_nsems; i++) {
335 			error = ds_copyout(&semaptr->sem_base[i].semval,
336 			    &arg->array[i], sizeof(arg->array[0]));
337 			if (error != 0)
338 				break;
339 		}
340 		break;
341 
342 	case GETZCNT:
343 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
344 			return (error);
345 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
346 			return (EINVAL);
347 		*retval = semaptr->sem_base[semnum].semzcnt;
348 		break;
349 
350 	case SETVAL:
351 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
352 			return (error);
353 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
354 			return (EINVAL);
355 		if (arg->val > seminfo.semvmx)
356 			return (ERANGE);
357 		semaptr->sem_base[semnum].semval = arg->val;
358 		semundo_clear(ix, semnum);
359 		wakeup(&sema[ix]);
360 		break;
361 
362 	case SETALL:
363 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
364 			return (error);
365 		semval = mallocarray(semaptr->sem_nsems, sizeof(arg->array[0]),
366 		    M_TEMP, M_WAITOK);
367 		for (i = 0; i < semaptr->sem_nsems; i++) {
368 			error = ds_copyin(&arg->array[i], &semval[i],
369 			    sizeof(arg->array[0]));
370 			if (error != 0)
371 				goto error;
372 			if (semval[i] > seminfo.semvmx) {
373 				error = ERANGE;
374 				goto error;
375 			}
376 		}
377 		for (i = 0; i < semaptr->sem_nsems; i++)
378 			semaptr->sem_base[i].semval = semval[i];
379 		semundo_clear(ix, -1);
380 		wakeup(&sema[ix]);
381 		break;
382 
383 	default:
384 		return (EINVAL);
385 	}
386 
387 error:
388 	if (semval)
389 		free(semval, M_TEMP,
390 		    semaptr->sem_nsems * sizeof(arg->array[0]));
391 
392 	return (error);
393 }
394 
395 int
396 sys_semget(struct proc *p, void *v, register_t *retval)
397 {
398 	struct sys_semget_args /* {
399 		syscallarg(key_t) key;
400 		syscallarg(int) nsems;
401 		syscallarg(int) semflg;
402 	} */ *uap = v;
403 	int semid, error;
404 	int key = SCARG(uap, key);
405 	int nsems = SCARG(uap, nsems);
406 	int semflg = SCARG(uap, semflg);
407 	struct semid_ds *semaptr, *semaptr_new = NULL;
408 	struct ucred *cred = p->p_ucred;
409 
410 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
411 
412 	/*
413 	 * Preallocate space for the new semaphore.  If we are going
414 	 * to sleep, we want to sleep now to eliminate any race
415 	 * condition in allocating a semaphore with a specific key.
416 	 */
417 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
418 		if (nsems <= 0 || nsems > seminfo.semmsl) {
419 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
420 			    seminfo.semmsl));
421 			return (EINVAL);
422 		}
423 		if (nsems > seminfo.semmns - semtot) {
424 			DPRINTF(("not enough semaphores left (need %d, got %d)\n",
425 			    nsems, seminfo.semmns - semtot));
426 			return (ENOSPC);
427 		}
428 		semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO);
429 		semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
430 		    M_SEM, M_WAITOK|M_ZERO);
431 	}
432 
433 	if (key != IPC_PRIVATE) {
434 		for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
435 			if ((semaptr = sema[semid]) != NULL &&
436 			    semaptr->sem_perm.key == key) {
437 				DPRINTF(("found public key\n"));
438 				if ((error = ipcperm(cred, &semaptr->sem_perm,
439 				    semflg & 0700)))
440 					goto error;
441 				if (nsems > 0 && semaptr->sem_nsems < nsems) {
442 					DPRINTF(("too small\n"));
443 					error = EINVAL;
444 					goto error;
445 				}
446 				if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
447 					DPRINTF(("not exclusive\n"));
448 					error = EEXIST;
449 					goto error;
450 				}
451 				if (semaptr_new != NULL) {
452 					free(semaptr_new->sem_base, M_SEM,
453 					    nsems * sizeof(struct sem));
454 					pool_put(&sema_pool, semaptr_new);
455 				}
456 				goto found;
457 			}
458 		}
459 	}
460 
461 	DPRINTF(("need to allocate the semid_ds\n"));
462 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
463 		for (semid = 0; semid < seminfo.semmni; semid++) {
464 			if ((semaptr = sema[semid]) == NULL)
465 				break;
466 		}
467 		if (semid == seminfo.semmni) {
468 			DPRINTF(("no more semid_ds's available\n"));
469 			error = ENOSPC;
470 			goto error;
471 		}
472 		DPRINTF(("semid %d is available\n", semid));
473 		semaptr_new->sem_perm.key = key;
474 		semaptr_new->sem_perm.cuid = cred->cr_uid;
475 		semaptr_new->sem_perm.uid = cred->cr_uid;
476 		semaptr_new->sem_perm.cgid = cred->cr_gid;
477 		semaptr_new->sem_perm.gid = cred->cr_gid;
478 		semaptr_new->sem_perm.mode = (semflg & 0777);
479 		semaptr_new->sem_perm.seq = semseqs[semid] =
480 		    (semseqs[semid] + 1) & 0x7fff;
481 		semaptr_new->sem_nsems = nsems;
482 		semaptr_new->sem_otime = 0;
483 		semaptr_new->sem_ctime = gettime();
484 		sema[semid] = semaptr_new;
485 		semtot += nsems;
486 	} else {
487 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
488 		return (ENOENT);
489 	}
490 
491 found:
492 	*retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
493 	return (0);
494 error:
495 	if (semaptr_new != NULL) {
496 		free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem));
497 		pool_put(&sema_pool, semaptr_new);
498 	}
499 	return (error);
500 }
501 
502 int
503 sys_semop(struct proc *p, void *v, register_t *retval)
504 {
505 	struct sys_semop_args /* {
506 		syscallarg(int) semid;
507 		syscallarg(struct sembuf *) sops;
508 		syscallarg(size_t) nsops;
509 	} */ *uap = v;
510 #define	NSOPS	8
511 	struct sembuf sopbuf[NSOPS];
512 	int semid = SCARG(uap, semid);
513 	size_t nsops = SCARG(uap, nsops);
514 	struct sembuf *sops;
515 	struct semid_ds *semaptr;
516 	struct sembuf *sopptr = NULL;
517 	struct sem *semptr = NULL;
518 	struct sem_undo *suptr = NULL;
519 	struct ucred *cred = p->p_ucred;
520 	size_t i, j;
521 	int do_wakeup, do_undos, error;
522 
523 	DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
524 	    (u_long)nsops));
525 
526 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
527 
528 	if (semid < 0 || semid >= seminfo.semmni)
529 		return (EINVAL);
530 
531 	if ((semaptr = sema[semid]) == NULL ||
532 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
533 		return (EINVAL);
534 
535 	if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
536 		DPRINTF(("error = %d from ipaccess\n", error));
537 		return (error);
538 	}
539 
540 	if (nsops == 0) {
541 		*retval = 0;
542 		return (0);
543 	} else if (nsops > (size_t)seminfo.semopm) {
544 		DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
545 		    (u_long)nsops));
546 		return (E2BIG);
547 	}
548 
549 	if (nsops <= NSOPS)
550 		sops = sopbuf;
551 	else
552 		sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
553 	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
554 	if (error != 0) {
555 		DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
556 		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
557 		goto done2;
558 	}
559 
560 	/*
561 	 * Loop trying to satisfy the vector of requests.
562 	 * If we reach a point where we must wait, any requests already
563 	 * performed are rolled back and we go to sleep until some other
564 	 * process wakes us up.  At this point, we start all over again.
565 	 *
566 	 * This ensures that from the perspective of other tasks, a set
567 	 * of requests is atomic (never partially satisfied).
568 	 */
569 	do_undos = 0;
570 
571 	for (;;) {
572 		do_wakeup = 0;
573 
574 		for (i = 0; i < nsops; i++) {
575 			sopptr = &sops[i];
576 
577 			if (sopptr->sem_num >= semaptr->sem_nsems) {
578 				error = EFBIG;
579 				goto done2;
580 			}
581 
582 			semptr = &semaptr->sem_base[sopptr->sem_num];
583 
584 			DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
585 			    semaptr, semaptr->sem_base, semptr,
586 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
587 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
588 
589 			if (sopptr->sem_op < 0) {
590 				if ((int)(semptr->semval +
591 					  sopptr->sem_op) < 0) {
592 					DPRINTF(("semop:  can't do it now\n"));
593 					break;
594 				} else {
595 					semptr->semval += sopptr->sem_op;
596 					if (semptr->semval == 0 &&
597 					    semptr->semzcnt > 0)
598 						do_wakeup = 1;
599 				}
600 				if (sopptr->sem_flg & SEM_UNDO)
601 					do_undos++;
602 			} else if (sopptr->sem_op == 0) {
603 				if (semptr->semval > 0) {
604 					DPRINTF(("semop:  not zero now\n"));
605 					break;
606 				}
607 			} else {
608 				if (semptr->semncnt > 0)
609 					do_wakeup = 1;
610 				semptr->semval += sopptr->sem_op;
611 				if (sopptr->sem_flg & SEM_UNDO)
612 					do_undos++;
613 			}
614 		}
615 
616 		/*
617 		 * Did we get through the entire vector and can we undo it?
618 		 */
619 		if (i >= nsops && do_undos <= SEMUME)
620 			goto done;
621 
622 		/*
623 		 * No ... rollback anything that we've already done
624 		 */
625 		DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
626 		for (j = 0; j < i; j++)
627 			semaptr->sem_base[sops[j].sem_num].semval -=
628 			    sops[j].sem_op;
629 
630 		/*
631 		 * Did we have too many SEM_UNDO's
632 		 */
633 		if (do_undos > SEMUME) {
634 			error = ENOSPC;
635 			goto done2;
636 		}
637 
638 		/*
639 		 * If the request that we couldn't satisfy has the
640 		 * NOWAIT flag set then return with EAGAIN.
641 		 */
642 		if (sopptr->sem_flg & IPC_NOWAIT) {
643 			error = EAGAIN;
644 			goto done2;
645 		}
646 
647 		if (sopptr->sem_op == 0)
648 			semptr->semzcnt++;
649 		else
650 			semptr->semncnt++;
651 
652 		DPRINTF(("semop:  good night!\n"));
653 		error = tsleep_nsec(&sema[semid], PLOCK | PCATCH,
654 		    "semwait", INFSLP);
655 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
656 
657 		suptr = NULL;	/* sem_undo may have been reallocated */
658 
659 		/*
660 		 * Make sure that the semaphore still exists
661 		 */
662 		if (sema[semid] == NULL ||
663 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
664 			error = EIDRM;
665 			goto done2;
666 		}
667 
668 		/*
669 		 * The semaphore is still alive.  Readjust the count of
670 		 * waiting processes.
671 		 */
672 		if (sopptr->sem_op == 0)
673 			semptr->semzcnt--;
674 		else
675 			semptr->semncnt--;
676 
677 		/*
678 		 * Is it really morning, or was our sleep interrupted?
679 		 * (Delayed check of tsleep() return code because we
680 		 * need to decrement sem[nz]cnt either way.)
681 		 */
682 		if (error != 0) {
683 			error = EINTR;
684 			goto done2;
685 		}
686 		DPRINTF(("semop:  good morning!\n"));
687 	}
688 
689 done:
690 	/*
691 	 * Process any SEM_UNDO requests.
692 	 */
693 	if (do_undos) {
694 		for (i = 0; i < nsops; i++) {
695 			/*
696 			 * We only need to deal with SEM_UNDO's for non-zero
697 			 * op's.
698 			 */
699 			int adjval;
700 
701 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
702 				continue;
703 			adjval = sops[i].sem_op;
704 			if (adjval == 0)
705 				continue;
706 			error = semundo_adjust(p, &suptr, semid,
707 			    sops[i].sem_num, -adjval);
708 			if (error == 0)
709 				continue;
710 
711 			/*
712 			 * Uh-Oh!  We ran out of either sem_undo's or undo's.
713 			 * Rollback the adjustments to this point and then
714 			 * rollback the semaphore ups and down so we can return
715 			 * with an error with all structures restored.  We
716 			 * rollback the undo's in the exact reverse order that
717 			 * we applied them.  This guarantees that we won't run
718 			 * out of space as we roll things back out.
719 			 */
720 			for (j = i; j > 0;) {
721 				j--;
722 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
723 					continue;
724 				adjval = sops[j].sem_op;
725 				if (adjval == 0)
726 					continue;
727 				if (semundo_adjust(p, &suptr, semid,
728 				    sops[j].sem_num, adjval) != 0)
729 					panic("semop - can't undo undos");
730 			}
731 
732 			for (j = 0; j < nsops; j++)
733 				semaptr->sem_base[sops[j].sem_num].semval -=
734 				    sops[j].sem_op;
735 
736 			DPRINTF(("error = %d from semundo_adjust\n", error));
737 			goto done2;
738 		} /* loop through the sops */
739 	} /* if (do_undos) */
740 
741 	/* We're definitely done - set the sempid's */
742 	for (i = 0; i < nsops; i++) {
743 		sopptr = &sops[i];
744 		semptr = &semaptr->sem_base[sopptr->sem_num];
745 		semptr->sempid = p->p_p->ps_pid;
746 	}
747 
748 	semaptr->sem_otime = gettime();
749 
750 	/* Do a wakeup if any semaphore was up'd. */
751 	if (do_wakeup) {
752 		DPRINTF(("semop:  doing wakeup\n"));
753 		wakeup(&sema[semid]);
754 		DPRINTF(("semop:  back from wakeup\n"));
755 	}
756 	DPRINTF(("semop:  done\n"));
757 	*retval = 0;
758 done2:
759 	if (sops != sopbuf)
760 		free(sops, M_SEM, nsops * sizeof(struct sembuf));
761 	return (error);
762 }
763 
764 /*
765  * Go through the undo structures for this process and apply the adjustments to
766  * semaphores.
767  */
768 void
769 semexit(struct process *pr)
770 {
771 	struct sem_undo *suptr;
772 	struct sem_undo **supptr;
773 
774 	/*
775 	 * Go through the chain of undo vectors looking for one associated with
776 	 * this process.  Remember the pointer to the pointer to the element
777 	 * to dequeue it later.
778 	 */
779 	supptr = &SLIST_FIRST(&semu_list);
780 	SLIST_FOREACH(suptr, &semu_list, un_next) {
781 		if (suptr->un_proc == pr)
782 			break;
783 		supptr = &SLIST_NEXT(suptr, un_next);
784 	}
785 
786 	/*
787 	 * If there is no undo vector, skip to the end.
788 	 */
789 	if (suptr == NULL)
790 		return;
791 
792 	/*
793 	 * We now have an undo vector for this process.
794 	 */
795 	DPRINTF(("process @%p has undo structure with %d entries\n", pr,
796 	    suptr->un_cnt));
797 
798 	/*
799 	 * If there are any active undo elements then process them.
800 	 */
801 	if (suptr->un_cnt > 0) {
802 		int ix;
803 
804 		for (ix = 0; ix < suptr->un_cnt; ix++) {
805 			int semid = suptr->un_ent[ix].un_id;
806 			int semnum = suptr->un_ent[ix].un_num;
807 			int adjval = suptr->un_ent[ix].un_adjval;
808 			struct semid_ds *semaptr;
809 
810 			if ((semaptr = sema[semid]) == NULL)
811 				panic("semexit - semid not allocated");
812 			if (semnum >= semaptr->sem_nsems)
813 				panic("semexit - semnum out of range");
814 
815 			DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
816 			    suptr->un_proc, suptr->un_ent[ix].un_id,
817 			    suptr->un_ent[ix].un_num,
818 			    suptr->un_ent[ix].un_adjval,
819 			    semaptr->sem_base[semnum].semval));
820 
821 			if (adjval < 0 &&
822 			    semaptr->sem_base[semnum].semval < -adjval)
823 				semaptr->sem_base[semnum].semval = 0;
824 			else
825 				semaptr->sem_base[semnum].semval += adjval;
826 
827 			wakeup(&sema[semid]);
828 			DPRINTF(("semexit:  back from wakeup\n"));
829 		}
830 	}
831 
832 	/*
833 	 * Deallocate the undo vector.
834 	 */
835 	DPRINTF(("removing vector\n"));
836 	*supptr = SLIST_NEXT(suptr, un_next);
837 	pool_put(&semu_pool, suptr);
838 	semutot--;
839 }
840 
841 /*
842  * Userland access to struct seminfo.
843  */
844 int
845 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
846 	void *newp, size_t newlen)
847 {
848 	int error, val;
849 	struct semid_ds **sema_new;
850 	unsigned short *newseqs;
851 
852 	if (namelen != 2) {
853 		switch (name[0]) {
854 		case KERN_SEMINFO_SEMMNI:
855 		case KERN_SEMINFO_SEMMNS:
856 		case KERN_SEMINFO_SEMMNU:
857 		case KERN_SEMINFO_SEMMSL:
858 		case KERN_SEMINFO_SEMOPM:
859 		case KERN_SEMINFO_SEMUME:
860 		case KERN_SEMINFO_SEMUSZ:
861 		case KERN_SEMINFO_SEMVMX:
862 		case KERN_SEMINFO_SEMAEM:
863 			break;
864 		default:
865                         return (ENOTDIR);       /* overloaded */
866                 }
867         }
868 
869 	switch (name[0]) {
870 	case KERN_SEMINFO_SEMMNI:
871 		val = seminfo.semmni;
872 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
873 		    val == seminfo.semmni)
874 			return (error);
875 
876 		if (val < seminfo.semmni || val > 0xffff)
877 			return (EINVAL);
878 
879 		/* Expand semsegs and semseqs arrays */
880 		sema_new = mallocarray(val, sizeof(struct semid_ds *),
881 		    M_SEM, M_WAITOK|M_ZERO);
882 		memcpy(sema_new, sema,
883 		    seminfo.semmni * sizeof(struct semid_ds *));
884 		newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
885 		    M_WAITOK|M_ZERO);
886 		memcpy(newseqs, semseqs,
887 		    seminfo.semmni * sizeof(unsigned short));
888 		free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *));
889 		free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short));
890 		sema = sema_new;
891 		semseqs = newseqs;
892 		seminfo.semmni = val;
893 		return (0);
894 	case KERN_SEMINFO_SEMMNS:
895 		val = seminfo.semmns;
896 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
897 		    val == seminfo.semmns)
898 			return (error);
899 		if (val < seminfo.semmns || val > 0xffff)
900 			return (EINVAL);	/* can't decrease semmns */
901 		seminfo.semmns = val;
902 		return (0);
903 	case KERN_SEMINFO_SEMMNU:
904 		val = seminfo.semmnu;
905 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
906 		    val == seminfo.semmnu)
907 			return (error);
908 		if (val < seminfo.semmnu)
909 			return (EINVAL);	/* can't decrease semmnu */
910 		seminfo.semmnu = val;
911 		return (0);
912 	case KERN_SEMINFO_SEMMSL:
913 		val = seminfo.semmsl;
914 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
915 		    val == seminfo.semmsl)
916 			return (error);
917 		if (val < seminfo.semmsl || val > 0xffff)
918 			return (EINVAL);	/* can't decrease semmsl */
919 		seminfo.semmsl = val;
920 		return (0);
921 	case KERN_SEMINFO_SEMOPM:
922 		val = seminfo.semopm;
923 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
924 		    val == seminfo.semopm)
925 			return (error);
926 		if (val <= 0)
927 			return (EINVAL);	/* semopm must be >= 1 */
928 		seminfo.semopm = val;
929 		return (0);
930 	case KERN_SEMINFO_SEMUME:
931 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
932 	case KERN_SEMINFO_SEMUSZ:
933 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
934 	case KERN_SEMINFO_SEMVMX:
935 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
936 	case KERN_SEMINFO_SEMAEM:
937 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
938 	default:
939 		return (EOPNOTSUPP);
940 	}
941 	/* NOTREACHED */
942 }
943