xref: /openbsd-src/sys/kern/sysv_sem.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: sysv_sem.c,v 1.19 2003/07/21 22:44:50 tedu 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.semmsl)
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 elliminate any race
426 	 * condition in allocating a semaphore with a specific key.
427 	 */
428 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
429 		semaptr_new = pool_get(&sema_pool, PR_WAITOK);
430 		semaptr_new->sem_base = malloc(nsems * sizeof(struct sem),
431 		    M_SEM, M_WAITOK);
432 		bzero(semaptr_new->sem_base, nsems * sizeof(struct sem));
433 	}
434 
435 	if (key != IPC_PRIVATE) {
436 		for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
437 			if ((semaptr = sema[semid]) != NULL &&
438 			    semaptr->sem_perm.key == key)
439 				break;
440 		}
441 		if (semaptr != NULL) {
442 			DPRINTF(("found public key\n"));
443 			if ((error = ipcperm(cred, &semaptr->sem_perm,
444 			    semflg & 0700)))
445 				goto error;
446 			if (nsems > 0 && semaptr->sem_nsems < nsems) {
447 				DPRINTF(("too small\n"));
448 				error = EINVAL;
449 				goto error;
450 			}
451 			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
452 				DPRINTF(("not exclusive\n"));
453 				error = EEXIST;
454 				goto error;
455 			}
456 			goto found;
457 		}
458 	}
459 
460 	DPRINTF(("need to allocate the semid_ds\n"));
461 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
462 		if (nsems <= 0 || nsems > seminfo.semmsl) {
463 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
464 			    seminfo.semmsl));
465 			error = EINVAL;
466 			goto error;
467 		}
468 		if (nsems > seminfo.semmns - semtot) {
469 			DPRINTF(("not enough semaphores left (need %d, got %d)\n",
470 			    nsems, seminfo.semmns - semtot));
471 			error = ENOSPC;
472 			goto error;
473 		}
474 		for (semid = 0; semid < seminfo.semmni; semid++) {
475 			if ((semaptr = sema[semid]) == NULL)
476 				break;
477 		}
478 		if (semid == seminfo.semmni) {
479 			DPRINTF(("no more semid_ds's available\n"));
480 			error = ENOSPC;
481 			goto error;
482 		}
483 		DPRINTF(("semid %d is available\n", semid));
484 		semaptr_new->sem_perm.key = key;
485 		semaptr_new->sem_perm.cuid = cred->cr_uid;
486 		semaptr_new->sem_perm.uid = cred->cr_uid;
487 		semaptr_new->sem_perm.cgid = cred->cr_gid;
488 		semaptr_new->sem_perm.gid = cred->cr_gid;
489 		semaptr_new->sem_perm.mode = (semflg & 0777);
490 		semaptr_new->sem_perm.seq = semseqs[semid] =
491 		    (semseqs[semid] + 1) & 0x7fff;
492 		semaptr_new->sem_nsems = nsems;
493 		semaptr_new->sem_otime = 0;
494 		semaptr_new->sem_ctime = time.tv_sec;
495 		sema[semid] = semaptr_new;
496 		semtot += nsems;
497 	} else {
498 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
499 		return (ENOENT);
500 	}
501 
502 found:
503 	*retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
504 	return (0);
505 error:
506 	if (semaptr_new != NULL) {
507 		free(semaptr_new->sem_base, M_SEM);
508 		pool_put(&sema_pool, semaptr_new);
509 	}
510 	return (error);
511 }
512 
513 int
514 sys_semop(struct proc *p, void *v, register_t *retval)
515 {
516 	struct sys_semop_args /* {
517 		syscallarg(int) semid;
518 		syscallarg(struct sembuf *) sops;
519 		syscallarg(u_int) nsops;
520 	} */ *uap = v;
521 	int semid = SCARG(uap, semid);
522 	u_int nsops = SCARG(uap, nsops);
523 	struct sembuf *sops;
524 	struct semid_ds *semaptr;
525 	struct sembuf *sopptr = NULL;
526 	struct sem *semptr = NULL;
527 	struct sem_undo *suptr = NULL;
528 	struct ucred *cred = p->p_ucred;
529 	int i, j, error;
530 	int do_wakeup, do_undos;
531 
532 	DPRINTF(("call to semop(%d, %p, %d)\n", semid, sops, nsops));
533 
534 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
535 
536 	if (semid < 0 || semid >= seminfo.semmsl)
537 		return (EINVAL);
538 
539 	if ((semaptr = sema[semid]) == NULL ||
540 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
541 		return (EINVAL);
542 
543 	if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
544 		DPRINTF(("error = %d from ipaccess\n", error));
545 		return (error);
546 	}
547 
548 	if (nsops > seminfo.semopm) {
549 		DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, nsops));
550 		return (E2BIG);
551 	}
552 
553 	sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK);
554 	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
555 	if (error != 0) {
556 		DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error,
557 		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
558 		free(sops, M_SEM);
559 		return (error);
560 	}
561 
562 	/*
563 	 * Loop trying to satisfy the vector of requests.
564 	 * If we reach a point where we must wait, any requests already
565 	 * performed are rolled back and we go to sleep until some other
566 	 * process wakes us up.  At this point, we start all over again.
567 	 *
568 	 * This ensures that from the perspective of other tasks, a set
569 	 * of requests is atomic (never partially satisfied).
570 	 */
571 	do_undos = 0;
572 
573 	for (;;) {
574 		do_wakeup = 0;
575 
576 		for (i = 0; i < nsops; i++) {
577 			sopptr = &sops[i];
578 
579 			if (sopptr->sem_num >= semaptr->sem_nsems) {
580 				free(sops, M_SEM);
581 				return (EFBIG);
582 			}
583 
584 			semptr = &semaptr->sem_base[sopptr->sem_num];
585 
586 			DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
587 			    semaptr, semaptr->sem_base, semptr,
588 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
589 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
590 
591 			if (sopptr->sem_op < 0) {
592 				if ((int)(semptr->semval +
593 					  sopptr->sem_op) < 0) {
594 					DPRINTF(("semop:  can't do it now\n"));
595 					break;
596 				} else {
597 					semptr->semval += sopptr->sem_op;
598 					if (semptr->semval == 0 &&
599 					    semptr->semzcnt > 0)
600 						do_wakeup = 1;
601 				}
602 				if (sopptr->sem_flg & SEM_UNDO)
603 					do_undos = 1;
604 			} else if (sopptr->sem_op == 0) {
605 				if (semptr->semval > 0) {
606 					DPRINTF(("semop:  not zero now\n"));
607 					break;
608 				}
609 			} else {
610 				if (semptr->semncnt > 0)
611 					do_wakeup = 1;
612 				semptr->semval += sopptr->sem_op;
613 				if (sopptr->sem_flg & SEM_UNDO)
614 					do_undos = 1;
615 			}
616 		}
617 
618 		/*
619 		 * Did we get through the entire vector?
620 		 */
621 		if (i >= nsops)
622 			goto done;
623 
624 		/*
625 		 * No ... rollback anything that we've already done
626 		 */
627 		DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
628 		for (j = 0; j < i; j++)
629 			semaptr->sem_base[sops[j].sem_num].semval -=
630 			    sops[j].sem_op;
631 
632 		/*
633 		 * If the request that we couldn't satisfy has the
634 		 * NOWAIT flag set then return with EAGAIN.
635 		 */
636 		if (sopptr->sem_flg & IPC_NOWAIT) {
637 			free(sops, M_SEM);
638 			return (EAGAIN);
639 		}
640 
641 		if (sopptr->sem_op == 0)
642 			semptr->semzcnt++;
643 		else
644 			semptr->semncnt++;
645 
646 		DPRINTF(("semop:  good night!\n"));
647 		error = tsleep(&sema[semid], PLOCK | PCATCH,
648 		    "semwait", 0);
649 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
650 
651 		suptr = NULL;	/* sem_undo may have been reallocated */
652 
653 		if (error != 0) {
654 			free(sops, M_SEM);
655 			return (EINTR);
656 		}
657 		DPRINTF(("semop:  good morning!\n"));
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 			free(sops, M_SEM);
665 			return (EIDRM);
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 done:
679 	/*
680 	 * Process any SEM_UNDO requests.
681 	 */
682 	if (do_undos) {
683 		for (i = 0; i < nsops; i++) {
684 			/*
685 			 * We only need to deal with SEM_UNDO's for non-zero
686 			 * op's.
687 			 */
688 			int adjval;
689 
690 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
691 				continue;
692 			adjval = sops[i].sem_op;
693 			if (adjval == 0)
694 				continue;
695 			error = semundo_adjust(p, &suptr, semid,
696 			    sops[i].sem_num, -adjval);
697 			if (error == 0)
698 				continue;
699 
700 			/*
701 			 * Uh-Oh!  We ran out of either sem_undo's or undo's.
702 			 * Rollback the adjustments to this point and then
703 			 * rollback the semaphore ups and down so we can return
704 			 * with an error with all structures restored.  We
705 			 * rollback the undo's in the exact reverse order that
706 			 * we applied them.  This guarantees that we won't run
707 			 * out of space as we roll things back out.
708 			 */
709 			for (j = i - 1; j >= 0; j--) {
710 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
711 					continue;
712 				adjval = sops[j].sem_op;
713 				if (adjval == 0)
714 					continue;
715 				if (semundo_adjust(p, &suptr, semid,
716 				    sops[j].sem_num, adjval) != 0)
717 					panic("semop - can't undo undos");
718 			}
719 
720 			for (j = 0; j < nsops; j++)
721 				semaptr->sem_base[sops[j].sem_num].semval -=
722 				    sops[j].sem_op;
723 
724 			DPRINTF(("error = %d from semundo_adjust\n", error));
725 			free(sops, M_SEM);
726 			return (error);
727 		} /* loop through the sops */
728 	} /* if (do_undos) */
729 
730 	/* We're definitely done - set the sempid's */
731 	for (i = 0; i < nsops; i++) {
732 		sopptr = &sops[i];
733 		semptr = &semaptr->sem_base[sopptr->sem_num];
734 		semptr->sempid = p->p_pid;
735 	}
736 	free(sops, M_SEM);
737 
738 	/* Do a wakeup if any semaphore was up'd. */
739 	if (do_wakeup) {
740 		DPRINTF(("semop:  doing wakeup\n"));
741 		wakeup(&sema[semid]);
742 		DPRINTF(("semop:  back from wakeup\n"));
743 	}
744 	DPRINTF(("semop:  done\n"));
745 	*retval = 0;
746 	return (0);
747 }
748 
749 /*
750  * Go through the undo structures for this process and apply the adjustments to
751  * semaphores.
752  */
753 void
754 semexit(struct proc *p)
755 {
756 	struct sem_undo *suptr;
757 	struct sem_undo **supptr;
758 
759 	/*
760 	 * Go through the chain of undo vectors looking for one associated with
761 	 * this process.
762 	 */
763 	for (supptr = &semu_list; (suptr = *supptr) != NULL;
764 	    supptr = &suptr->un_next) {
765 		if (suptr->un_proc == p)
766 			break;
767 	}
768 
769 	/*
770 	 * No (i.e. we are in case 1 or 2).
771 	 *
772 	 * If there is no undo vector, skip to the end and unlock the
773 	 * semaphore facility if necessary.
774 	 */
775 	if (suptr == NULL)
776 		return;
777 
778 	/*
779 	 * We are now in case 1 or 2, and we have an undo vector for this
780 	 * process.
781 	 */
782 	DPRINTF(("proc @%p has undo structure with %d entries\n", p,
783 	    suptr->un_cnt));
784 
785 	/*
786 	 * If there are any active undo elements then process them.
787 	 */
788 	if (suptr->un_cnt > 0) {
789 		int ix;
790 
791 		for (ix = 0; ix < suptr->un_cnt; ix++) {
792 			int semid = suptr->un_ent[ix].un_id;
793 			int semnum = suptr->un_ent[ix].un_num;
794 			int adjval = suptr->un_ent[ix].un_adjval;
795 			struct semid_ds *semaptr;
796 
797 			if ((semaptr = sema[semid]) == NULL)
798 				panic("semexit - semid not allocated");
799 			if (semnum >= semaptr->sem_nsems)
800 				panic("semexit - semnum out of range");
801 
802 			DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
803 			    suptr->un_proc, suptr->un_ent[ix].un_id,
804 			    suptr->un_ent[ix].un_num,
805 			    suptr->un_ent[ix].un_adjval,
806 			    semaptr->sem_base[semnum].semval));
807 
808 			if (adjval < 0 &&
809 			    semaptr->sem_base[semnum].semval < -adjval)
810 				semaptr->sem_base[semnum].semval = 0;
811 			else
812 				semaptr->sem_base[semnum].semval += adjval;
813 
814 			wakeup(&sema[semid]);
815 			DPRINTF(("semexit:  back from wakeup\n"));
816 		}
817 	}
818 
819 	/*
820 	 * Deallocate the undo vector.
821 	 */
822 	DPRINTF(("removing vector\n"));
823 	*supptr = suptr->un_next;
824 	pool_put(&semu_pool, suptr);
825 	semutot--;
826 }
827 
828 /*
829  * Userland access to struct seminfo.
830  */
831 int
832 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
833 	void *newp, size_t newlen)
834 {
835 	int error, val;
836 	struct semid_ds **sema_new;
837 	unsigned short *newseqs;
838 
839 	if (namelen != 2) {
840 		switch (name[0]) {
841 		case KERN_SEMINFO_SEMMNI:
842 		case KERN_SEMINFO_SEMMNS:
843 		case KERN_SEMINFO_SEMMNU:
844 		case KERN_SEMINFO_SEMMSL:
845 		case KERN_SEMINFO_SEMOPM:
846 		case KERN_SEMINFO_SEMUME:
847 		case KERN_SEMINFO_SEMUSZ:
848 		case KERN_SEMINFO_SEMVMX:
849 		case KERN_SEMINFO_SEMAEM:
850 			break;
851 		default:
852                         return (ENOTDIR);       /* overloaded */
853                 }
854         }
855 
856 	switch (name[0]) {
857 	case KERN_SEMINFO_SEMMNI:
858 		val = seminfo.semmni;
859 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
860 		    val == seminfo.semmni)
861 			return (error);
862 
863 		if (val < seminfo.semmni || val > 0xffff)
864 			return (EINVAL);
865 
866 		/* Expand semsegs and semseqs arrays */
867 		sema_new = malloc(val * sizeof(struct semid_ds *),
868 		    M_SEM, M_WAITOK);
869 		bcopy(sema, sema_new,
870 		    seminfo.semmni * sizeof(struct semid_ds *));
871 		bzero(sema_new + seminfo.semmni,
872 		    (val - seminfo.semmni) * sizeof(struct semid_ds *));
873 		newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK);
874 		bcopy(semseqs, newseqs,
875 		    seminfo.semmni * sizeof(unsigned short));
876 		bzero(newseqs + seminfo.semmni,
877 		    (val - seminfo.semmni) * sizeof(unsigned short));
878 		free(sema, M_SEM);
879 		free(semseqs, M_SEM);
880 		sema = sema_new;
881 		semseqs = newseqs;
882 		seminfo.semmni = val;
883 		return (0);
884 	case KERN_SEMINFO_SEMMNS:
885 		val = seminfo.semmns;
886 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
887 		    val == seminfo.semmns)
888 			return (error);
889 		if (val < seminfo.semmns)
890 			return (EINVAL);	/* can't decrease semmns */
891 		seminfo.semmns = val;
892 		return (0);
893 	case KERN_SEMINFO_SEMMNU:
894 		val = seminfo.semmnu;
895 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
896 		    val == seminfo.semmnu)
897 			return (error);
898 		if (val < seminfo.semmnu)
899 			return (EINVAL);	/* can't decrease semmnu */
900 		seminfo.semmnu = val;
901 		return (0);
902 	case KERN_SEMINFO_SEMMSL:
903 		val = seminfo.semmsl;
904 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
905 		    val == seminfo.semmsl)
906 			return (error);
907 		if (val < seminfo.semmsl)
908 			return (EINVAL);	/* can't decrease semmsl */
909 		seminfo.semmsl = val;
910 		return (0);
911 	case KERN_SEMINFO_SEMOPM:
912 		val = seminfo.semopm;
913 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
914 		    val == seminfo.semopm)
915 			return (error);
916 		if (val <= 0)
917 			return (EINVAL);	/* semopm must be >= 1 */
918 		seminfo.semopm = val;
919 		return (0);
920 	case KERN_SEMINFO_SEMUME:
921 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
922 	case KERN_SEMINFO_SEMUSZ:
923 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
924 	case KERN_SEMINFO_SEMVMX:
925 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
926 	case KERN_SEMINFO_SEMAEM:
927 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
928 	default:
929 		return (EOPNOTSUPP);
930 	}
931 	/* NOTREACHED */
932 }
933