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