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