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