xref: /openbsd-src/sys/kern/sysv_sem.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: sysv_sem.c,v 1.6 2001/08/12 22:50:12 millert Exp $	*/
2 /*	$NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $	*/
3 
4 /*
5  * Implementation of SVID semaphores
6  *
7  * Author:  Daniel Boulet
8  *
9  * This software is provided ``AS IS'' without any warranties of any kind.
10  */
11 
12 #include <sys/param.h>
13 #include <sys/systm.h>
14 #include <sys/kernel.h>
15 #include <sys/proc.h>
16 #include <sys/sem.h>
17 #include <sys/malloc.h>
18 
19 #include <sys/mount.h>
20 #include <sys/syscallargs.h>
21 
22 int	semtot = 0;
23 struct	semid_ds *sema;		/* semaphore id pool */
24 struct	sem *sem;		/* semaphore pool */
25 struct	sem_undo *semu_list;	/* list of active undo structures */
26 int	*semu;			/* undo structure pool */
27 
28 struct sem_undo *semu_alloc __P((struct proc *));
29 int semundo_adjust __P((struct proc *, struct sem_undo **, int, int, int));
30 void semundo_clear __P((int, int));
31 
32 void
33 seminit()
34 {
35 	register int i;
36 
37 	if (sema == NULL)
38 		panic("sema is NULL");
39 	if (semu == NULL)
40 		panic("semu is NULL");
41 
42 	for (i = 0; i < seminfo.semmni; i++) {
43 		sema[i].sem_base = 0;
44 		sema[i].sem_perm.mode = 0;
45 	}
46 	for (i = 0; i < seminfo.semmnu; i++) {
47 		register struct sem_undo *suptr = SEMU(i);
48 		suptr->un_proc = NULL;
49 	}
50 	semu_list = NULL;
51 }
52 
53 /*
54  * Allocate a new sem_undo structure for a process
55  * (returns ptr to structure or NULL if no more room)
56  */
57 struct sem_undo *
58 semu_alloc(p)
59 	struct proc *p;
60 {
61 	register int i;
62 	register struct sem_undo *suptr;
63 	register struct sem_undo **supptr;
64 	int attempt;
65 
66 	/*
67 	 * Try twice to allocate something.
68 	 * (we'll purge any empty structures after the first pass so
69 	 * two passes are always enough)
70 	 */
71 
72 	for (attempt = 0; attempt < 2; attempt++) {
73 		/*
74 		 * Look for a free structure.
75 		 * Fill it in and return it if we find one.
76 		 */
77 
78 		for (i = 0; i < seminfo.semmnu; i++) {
79 			suptr = SEMU(i);
80 			if (suptr->un_proc == NULL) {
81 				suptr->un_next = semu_list;
82 				semu_list = suptr;
83 				suptr->un_cnt = 0;
84 				suptr->un_proc = p;
85 				return(suptr);
86 			}
87 		}
88 
89 		/*
90 		 * We didn't find a free one, if this is the first attempt
91 		 * then try to free some structures.
92 		 */
93 
94 		if (attempt == 0) {
95 			/* All the structures are in use - try to free some */
96 			int did_something = 0;
97 
98 			supptr = &semu_list;
99 			while ((suptr = *supptr) != NULL) {
100 				if (suptr->un_cnt == 0)  {
101 					suptr->un_proc = NULL;
102 					*supptr = suptr->un_next;
103 					did_something = 1;
104 				} else
105 					supptr = &(suptr->un_next);
106 			}
107 
108 			/* If we didn't free anything then just give-up */
109 			if (!did_something)
110 				return(NULL);
111 		} else {
112 			/*
113 			 * The second pass failed even though we freed
114 			 * something after the first pass!
115 			 * This is IMPOSSIBLE!
116 			 */
117 			panic("semu_alloc - second attempt failed");
118 		}
119 	}
120 	return NULL;
121 }
122 
123 /*
124  * Adjust a particular entry for a particular proc
125  */
126 int
127 semundo_adjust(p, supptr, semid, semnum, adjval)
128 	register struct proc *p;
129 	struct sem_undo **supptr;
130 	int semid, semnum;
131 	int adjval;
132 {
133 	register struct sem_undo *suptr;
134 	register struct undo *sunptr;
135 	int i;
136 
137 	/* Look for and remember the sem_undo if the caller doesn't provide
138 	   it */
139 
140 	suptr = *supptr;
141 	if (suptr == NULL) {
142 		for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
143 			if (suptr->un_proc == p) {
144 				*supptr = suptr;
145 				break;
146 			}
147 		}
148 		if (suptr == NULL) {
149 			if (adjval == 0)
150 				return(0);
151 			suptr = semu_alloc(p);
152 			if (suptr == NULL)
153 				return(ENOSPC);
154 			*supptr = suptr;
155 		}
156 	}
157 
158 	/*
159 	 * Look for the requested entry and adjust it
160 	 * (delete if adjval becomes 0).
161 	 */
162 	sunptr = &suptr->un_ent[0];
163 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
164 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
165 			continue;
166 		if (adjval == 0)
167 			sunptr->un_adjval = 0;
168 		else
169 			sunptr->un_adjval += adjval;
170 		if (sunptr->un_adjval == 0) {
171 			suptr->un_cnt--;
172 			if (i < suptr->un_cnt)
173 				suptr->un_ent[i] =
174 				    suptr->un_ent[suptr->un_cnt];
175 		}
176 		return(0);
177 	}
178 
179 	/* Didn't find the right entry - create it */
180 	if (adjval == 0)
181 		return(0);
182 	if (suptr->un_cnt == SEMUME)
183 		return(EINVAL);
184 
185 	sunptr = &suptr->un_ent[suptr->un_cnt];
186 	suptr->un_cnt++;
187 	sunptr->un_adjval = adjval;
188 	sunptr->un_id = semid;
189 	sunptr->un_num = semnum;
190 	return(0);
191 }
192 
193 void
194 semundo_clear(semid, semnum)
195 	int semid, semnum;
196 {
197 	register struct sem_undo *suptr;
198 
199 	for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
200 		register struct undo *sunptr;
201 		register int i;
202 
203 		sunptr = &suptr->un_ent[0];
204 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
205 			if (sunptr->un_id == semid) {
206 				if (semnum == -1 || sunptr->un_num == semnum) {
207 					suptr->un_cnt--;
208 					if (i < suptr->un_cnt) {
209 						suptr->un_ent[i] =
210 						  suptr->un_ent[suptr->un_cnt];
211 						i--, sunptr--;
212 					}
213 				}
214 				if (semnum != -1)
215 					break;
216 			}
217 		}
218 	}
219 }
220 
221 void
222 semid_n2o(n, o)
223 	struct semid_ds *n;
224 	struct osemid_ds *o;
225 {
226 	o->sem_base = n->sem_base;
227 	o->sem_nsems = n->sem_nsems;
228 	o->sem_otime = n->sem_otime;
229 	o->sem_pad1 = n->sem_pad1;
230 	o->sem_ctime = n->sem_ctime;
231 	o->sem_pad2 = n->sem_pad2;
232 	bcopy(n->sem_pad3, o->sem_pad3, sizeof o->sem_pad3);
233 	ipc_n2o(&n->sem_perm, &o->sem_perm);
234 }
235 
236 int
237 sys___semctl(p, v, retval)
238 	struct proc *p;
239 	register void *v;
240 	register_t *retval;
241 {
242 	register struct sys___semctl_args /* {
243 		syscallarg(int) semid;
244 		syscallarg(int) semnum;
245 		syscallarg(int) cmd;
246 		syscallarg(union semun *) arg;
247 	} */ *uap = v;
248 	int semid = SCARG(uap, semid);
249 	int semnum = SCARG(uap, semnum);
250 	int cmd = SCARG(uap, cmd);
251 	union semun *arg = SCARG(uap, arg);
252 	union semun real_arg;
253 	struct ucred *cred = p->p_ucred;
254 	int i, rval, eval;
255 	struct semid_ds sbuf;
256 	register struct semid_ds *semaptr;
257 
258 #ifdef SEM_DEBUG
259 	printf("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg);
260 #endif
261 
262 	semid = IPCID_TO_IX(semid);
263 	if (semid < 0 || semid >= seminfo.semmsl)
264 		return(EINVAL);
265 
266 	semaptr = &sema[semid];
267 	if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
268 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
269 		return(EINVAL);
270 
271 	eval = 0;
272 	rval = 0;
273 
274 	switch (cmd) {
275 	case IPC_RMID:
276 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
277 			return(eval);
278 		semaptr->sem_perm.cuid = cred->cr_uid;
279 		semaptr->sem_perm.uid = cred->cr_uid;
280 		semtot -= semaptr->sem_nsems;
281 		for (i = semaptr->sem_base - sem; i < semtot; i++)
282 			sem[i] = sem[i + semaptr->sem_nsems];
283 		for (i = 0; i < seminfo.semmni; i++) {
284 			if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
285 			    sema[i].sem_base > semaptr->sem_base)
286 				sema[i].sem_base -= semaptr->sem_nsems;
287 		}
288 		semaptr->sem_perm.mode = 0;
289 		semundo_clear(semid, -1);
290 		wakeup((caddr_t)semaptr);
291 		break;
292 
293 	case IPC_SET:
294 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
295 			return(eval);
296 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
297 			return(eval);
298 		if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf,
299 		    sizeof(sbuf))) != 0)
300 			return(eval);
301 		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
302 		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
303 		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
304 		    (sbuf.sem_perm.mode & 0777);
305 		semaptr->sem_ctime = time.tv_sec;
306 		break;
307 
308 	case IPC_STAT:
309 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
310 			return(eval);
311 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
312 			return(eval);
313 		eval = copyout((caddr_t)semaptr, real_arg.buf,
314 		    sizeof(struct semid_ds));
315 		break;
316 
317 	case GETNCNT:
318 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
319 			return(eval);
320 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
321 			return(EINVAL);
322 		rval = semaptr->sem_base[semnum].semncnt;
323 		break;
324 
325 	case GETPID:
326 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
327 			return(eval);
328 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
329 			return(EINVAL);
330 		rval = semaptr->sem_base[semnum].sempid;
331 		break;
332 
333 	case GETVAL:
334 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
335 			return(eval);
336 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
337 			return(EINVAL);
338 		rval = semaptr->sem_base[semnum].semval;
339 		break;
340 
341 	case GETALL:
342 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
343 			return(eval);
344 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
345 			return(eval);
346 		for (i = 0; i < semaptr->sem_nsems; i++) {
347 			eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
348 			    &real_arg.array[i], sizeof(real_arg.array[0]));
349 			if (eval != 0)
350 				break;
351 		}
352 		break;
353 
354 	case GETZCNT:
355 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
356 			return(eval);
357 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
358 			return(EINVAL);
359 		rval = semaptr->sem_base[semnum].semzcnt;
360 		break;
361 
362 	case SETVAL:
363 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
364 			return(eval);
365 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
366 			return(EINVAL);
367 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
368 			return(eval);
369 		semaptr->sem_base[semnum].semval = real_arg.val;
370 		semundo_clear(semid, semnum);
371 		wakeup((caddr_t)semaptr);
372 		break;
373 
374 	case SETALL:
375 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
376 			return(eval);
377 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
378 			return(eval);
379 		for (i = 0; i < semaptr->sem_nsems; i++) {
380 			eval = copyin(&real_arg.array[i],
381 			    (caddr_t)&semaptr->sem_base[i].semval,
382 			    sizeof(real_arg.array[0]));
383 			if (eval != 0)
384 				break;
385 		}
386 		semundo_clear(semid, -1);
387 		wakeup((caddr_t)semaptr);
388 		break;
389 
390 	default:
391 		return(EINVAL);
392 	}
393 
394 	if (eval == 0)
395 		*retval = rval;
396 	return(eval);
397 }
398 
399 int
400 sys_semget(p, v, retval)
401 	struct proc *p;
402 	void *v;
403 	register_t *retval;
404 {
405 	register struct sys_semget_args /* {
406 		syscallarg(key_t) key;
407 		syscallarg(int) nsems;
408 		syscallarg(int) semflg;
409 	} */ *uap = v;
410 	int semid, eval;
411 	int key = SCARG(uap, key);
412 	int nsems = SCARG(uap, nsems);
413 	int semflg = SCARG(uap, semflg);
414 	struct ucred *cred = p->p_ucred;
415 
416 #ifdef SEM_DEBUG
417 	printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
418 #endif
419 
420 	if (key != IPC_PRIVATE) {
421 		for (semid = 0; semid < seminfo.semmni; semid++) {
422 			if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
423 			    sema[semid].sem_perm.key == key)
424 				break;
425 		}
426 		if (semid < seminfo.semmni) {
427 #ifdef SEM_DEBUG
428 			printf("found public key\n");
429 #endif
430 			if ((eval = ipcperm(cred, &sema[semid].sem_perm,
431 			    semflg & 0700)))
432 				return(eval);
433 			if (nsems > 0 && sema[semid].sem_nsems < nsems) {
434 #ifdef SEM_DEBUG
435 				printf("too small\n");
436 #endif
437 				return(EINVAL);
438 			}
439 			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
440 #ifdef SEM_DEBUG
441 				printf("not exclusive\n");
442 #endif
443 				return(EEXIST);
444 			}
445 			goto found;
446 		}
447 	}
448 
449 #ifdef SEM_DEBUG
450 	printf("need to allocate the semid_ds\n");
451 #endif
452 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
453 		if (nsems <= 0 || nsems > seminfo.semmsl) {
454 #ifdef SEM_DEBUG
455 			printf("nsems out of range (0<%d<=%d)\n", nsems,
456 			    seminfo.semmsl);
457 #endif
458 			return(EINVAL);
459 		}
460 		if (nsems > seminfo.semmns - semtot) {
461 #ifdef SEM_DEBUG
462 			printf("not enough semaphores left (need %d, got %d)\n",
463 			    nsems, seminfo.semmns - semtot);
464 #endif
465 			return(ENOSPC);
466 		}
467 		for (semid = 0; semid < seminfo.semmni; semid++) {
468 			if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
469 				break;
470 		}
471 		if (semid == seminfo.semmni) {
472 #ifdef SEM_DEBUG
473 			printf("no more semid_ds's available\n");
474 #endif
475 			return(ENOSPC);
476 		}
477 #ifdef SEM_DEBUG
478 		printf("semid %d is available\n", semid);
479 #endif
480 		sema[semid].sem_perm.key = key;
481 		sema[semid].sem_perm.cuid = cred->cr_uid;
482 		sema[semid].sem_perm.uid = cred->cr_uid;
483 		sema[semid].sem_perm.cgid = cred->cr_gid;
484 		sema[semid].sem_perm.gid = cred->cr_gid;
485 		sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
486 		sema[semid].sem_perm.seq =
487 		    (sema[semid].sem_perm.seq + 1) & 0x7fff;
488 		sema[semid].sem_nsems = nsems;
489 		sema[semid].sem_otime = 0;
490 		sema[semid].sem_ctime = time.tv_sec;
491 		sema[semid].sem_base = &sem[semtot];
492 		semtot += nsems;
493 		bzero(sema[semid].sem_base,
494 		    sizeof(sema[semid].sem_base[0])*nsems);
495 #ifdef SEM_DEBUG
496 		printf("sembase = %p, next = %p\n", sema[semid].sem_base,
497 		    &sem[semtot]);
498 #endif
499 	} else {
500 #ifdef SEM_DEBUG
501 		printf("didn't find it and wasn't asked to create it\n");
502 #endif
503 		return(ENOENT);
504 	}
505 
506 found:
507 	*retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
508 	return(0);
509 }
510 
511 int
512 sys_semop(p, v, retval)
513 	struct proc *p;
514 	void *v;
515 	register_t *retval;
516 {
517 	register struct sys_semop_args /* {
518 		syscallarg(int) semid;
519 		syscallarg(struct sembuf *) sops;
520 		syscallarg(u_int) nsops;
521 	} */ *uap = v;
522 	int semid = SCARG(uap, semid);
523 	int nsops = SCARG(uap, nsops);
524 	struct sembuf sops[MAX_SOPS];
525 	register struct semid_ds *semaptr;
526 	register struct sembuf *sopptr = NULL;
527 	register struct sem *semptr = NULL;
528 	struct sem_undo *suptr = NULL;
529 	struct ucred *cred = p->p_ucred;
530 	int i, j, eval;
531 	int do_wakeup, do_undos;
532 
533 #ifdef SEM_DEBUG
534 	printf("call to semop(%d, %p, %d)\n", semid, sops, nsops);
535 #endif
536 
537 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
538 
539 	if (semid < 0 || semid >= seminfo.semmsl)
540 		return(EINVAL);
541 
542 	semaptr = &sema[semid];
543 	if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
544 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
545 		return(EINVAL);
546 
547 	if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
548 #ifdef SEM_DEBUG
549 		printf("eval = %d from ipaccess\n", eval);
550 #endif
551 		return(eval);
552 	}
553 
554 	if (nsops > MAX_SOPS) {
555 #ifdef SEM_DEBUG
556 		printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
557 #endif
558 		return(E2BIG);
559 	}
560 
561 	if ((eval = copyin(SCARG(uap, sops), sops, nsops * sizeof(sops[0])))
562 	    != 0) {
563 #ifdef SEM_DEBUG
564 		printf("eval = %d from copyin(%p, %p, %d)\n", eval,
565 		    SCARG(uap, sops), &sops, nsops * sizeof(sops[0]));
566 #endif
567 		return(eval);
568 	}
569 
570 	/*
571 	 * Loop trying to satisfy the vector of requests.
572 	 * If we reach a point where we must wait, any requests already
573 	 * performed are rolled back and we go to sleep until some other
574 	 * process wakes us up.  At this point, we start all over again.
575 	 *
576 	 * This ensures that from the perspective of other tasks, a set
577 	 * of requests is atomic (never partially satisfied).
578 	 */
579 	do_undos = 0;
580 
581 	for (;;) {
582 		do_wakeup = 0;
583 
584 		for (i = 0; i < nsops; i++) {
585 			sopptr = &sops[i];
586 
587 			if (sopptr->sem_num >= semaptr->sem_nsems)
588 				return(EFBIG);
589 
590 			semptr = &semaptr->sem_base[sopptr->sem_num];
591 
592 #ifdef SEM_DEBUG
593 			printf("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
594 			    semaptr, semaptr->sem_base, semptr,
595 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
596 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
597 #endif
598 
599 			if (sopptr->sem_op < 0) {
600 				if ((int)(semptr->semval +
601 					  sopptr->sem_op) < 0) {
602 #ifdef SEM_DEBUG
603 					printf("semop:  can't do it now\n");
604 #endif
605 					break;
606 				} else {
607 					semptr->semval += sopptr->sem_op;
608 					if (semptr->semval == 0 &&
609 					    semptr->semzcnt > 0)
610 						do_wakeup = 1;
611 				}
612 				if (sopptr->sem_flg & SEM_UNDO)
613 					do_undos = 1;
614 			} else if (sopptr->sem_op == 0) {
615 				if (semptr->semval > 0) {
616 #ifdef SEM_DEBUG
617 					printf("semop:  not zero now\n");
618 #endif
619 					break;
620 				}
621 			} else {
622 				if (semptr->semncnt > 0)
623 					do_wakeup = 1;
624 				semptr->semval += sopptr->sem_op;
625 				if (sopptr->sem_flg & SEM_UNDO)
626 					do_undos = 1;
627 			}
628 		}
629 
630 		/*
631 		 * Did we get through the entire vector?
632 		 */
633 		if (i >= nsops)
634 			goto done;
635 
636 		/*
637 		 * No ... rollback anything that we've already done
638 		 */
639 #ifdef SEM_DEBUG
640 		printf("semop:  rollback 0 through %d\n", i-1);
641 #endif
642 		for (j = 0; j < i; j++)
643 			semaptr->sem_base[sops[j].sem_num].semval -=
644 			    sops[j].sem_op;
645 
646 		/*
647 		 * If the request that we couldn't satisfy has the
648 		 * NOWAIT flag set then return with EAGAIN.
649 		 */
650 		if (sopptr->sem_flg & IPC_NOWAIT)
651 			return(EAGAIN);
652 
653 		if (sopptr->sem_op == 0)
654 			semptr->semzcnt++;
655 		else
656 			semptr->semncnt++;
657 
658 #ifdef SEM_DEBUG
659 		printf("semop:  good night!\n");
660 #endif
661 		eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
662 		    "semwait", 0);
663 #ifdef SEM_DEBUG
664 		printf("semop:  good morning (eval=%d)!\n", eval);
665 #endif
666 
667 		suptr = NULL;	/* sem_undo may have been reallocated */
668 
669 		if (eval != 0)
670 			return(EINTR);
671 #ifdef SEM_DEBUG
672 		printf("semop:  good morning!\n");
673 #endif
674 
675 		/*
676 		 * Make sure that the semaphore still exists
677 		 */
678 		if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
679 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
680 			/* The man page says to return EIDRM. */
681 			/* Unfortunately, BSD doesn't define that code! */
682 #ifdef EIDRM
683 			return(EIDRM);
684 #else
685 			return(EINVAL);
686 #endif
687 		}
688 
689 		/*
690 		 * The semaphore is still alive.  Readjust the count of
691 		 * waiting processes.
692 		 */
693 		if (sopptr->sem_op == 0)
694 			semptr->semzcnt--;
695 		else
696 			semptr->semncnt--;
697 	}
698 
699 done:
700 	/*
701 	 * Process any SEM_UNDO requests.
702 	 */
703 	if (do_undos) {
704 		for (i = 0; i < nsops; i++) {
705 			/*
706 			 * We only need to deal with SEM_UNDO's for non-zero
707 			 * op's.
708 			 */
709 			int adjval;
710 
711 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
712 				continue;
713 			adjval = sops[i].sem_op;
714 			if (adjval == 0)
715 				continue;
716 			eval = semundo_adjust(p, &suptr, semid,
717 			    sops[i].sem_num, -adjval);
718 			if (eval == 0)
719 				continue;
720 
721 			/*
722 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
723 			 * Rollback the adjustments to this point and then
724 			 * rollback the semaphore ups and down so we can return
725 			 * with an error with all structures restored.  We
726 			 * rollback the undo's in the exact reverse order that
727 			 * we applied them.  This guarantees that we won't run
728 			 * out of space as we roll things back out.
729 			 */
730 			for (j = i - 1; j >= 0; j--) {
731 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
732 					continue;
733 				adjval = sops[j].sem_op;
734 				if (adjval == 0)
735 					continue;
736 				if (semundo_adjust(p, &suptr, semid,
737 				    sops[j].sem_num, adjval) != 0)
738 					panic("semop - can't undo undos");
739 			}
740 
741 			for (j = 0; j < nsops; j++)
742 				semaptr->sem_base[sops[j].sem_num].semval -=
743 				    sops[j].sem_op;
744 
745 #ifdef SEM_DEBUG
746 			printf("eval = %d from semundo_adjust\n", eval);
747 #endif
748 			return(eval);
749 		} /* loop through the sops */
750 	} /* if (do_undos) */
751 
752 	/* We're definitely done - set the sempid's */
753 	for (i = 0; i < nsops; i++) {
754 		sopptr = &sops[i];
755 		semptr = &semaptr->sem_base[sopptr->sem_num];
756 		semptr->sempid = p->p_pid;
757 	}
758 
759 	/* Do a wakeup if any semaphore was up'd. */
760 	if (do_wakeup) {
761 #ifdef SEM_DEBUG
762 		printf("semop:  doing wakeup\n");
763 #ifdef SEM_WAKEUP
764 		sem_wakeup((caddr_t)semaptr);
765 #else
766 		wakeup((caddr_t)semaptr);
767 #endif
768 		printf("semop:  back from wakeup\n");
769 #else
770 		wakeup((caddr_t)semaptr);
771 #endif
772 	}
773 #ifdef SEM_DEBUG
774 	printf("semop:  done\n");
775 #endif
776 	*retval = 0;
777 	return(0);
778 }
779 
780 /*
781  * Go through the undo structures for this process and apply the adjustments to
782  * semaphores.
783  */
784 void
785 semexit(p)
786 	struct proc *p;
787 {
788 	register struct sem_undo *suptr;
789 	register struct sem_undo **supptr;
790 
791 	/*
792 	 * Go through the chain of undo vectors looking for one associated with
793 	 * this process.
794 	 */
795 
796 	for (supptr = &semu_list; (suptr = *supptr) != NULL;
797 	    supptr = &suptr->un_next) {
798 		if (suptr->un_proc == p)
799 			break;
800 	}
801 
802 	/*
803 	 * No (i.e. we are in case 1 or 2).
804 	 *
805 	 * If there is no undo vector, skip to the end and unlock the
806 	 * semaphore facility if necessary.
807 	 */
808 	if (suptr == NULL)
809 		return;
810 
811 	/*
812 	 * We are now in case 1 or 2, and we have an undo vector for this
813 	 * process.
814 	 */
815 
816 #ifdef SEM_DEBUG
817 	printf("proc @%p has undo structure with %d entries\n", p,
818 	    suptr->un_cnt);
819 #endif
820 
821 	/*
822 	 * If there are any active undo elements then process them.
823 	 */
824 	if (suptr->un_cnt > 0) {
825 		int ix;
826 
827 		for (ix = 0; ix < suptr->un_cnt; ix++) {
828 			int semid = suptr->un_ent[ix].un_id;
829 			int semnum = suptr->un_ent[ix].un_num;
830 			int adjval = suptr->un_ent[ix].un_adjval;
831 			struct semid_ds *semaptr;
832 
833 			semaptr = &sema[semid];
834 			if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
835 				panic("semexit - semid not allocated");
836 			if (semnum >= semaptr->sem_nsems)
837 				panic("semexit - semnum out of range");
838 
839 #ifdef SEM_DEBUG
840 			printf("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
841 			    suptr->un_proc, suptr->un_ent[ix].un_id,
842 			    suptr->un_ent[ix].un_num,
843 			    suptr->un_ent[ix].un_adjval,
844 			    semaptr->sem_base[semnum].semval);
845 #endif
846 
847 			if (adjval < 0 &&
848 			    semaptr->sem_base[semnum].semval < -adjval)
849 				semaptr->sem_base[semnum].semval = 0;
850 			else
851 				semaptr->sem_base[semnum].semval += adjval;
852 
853 #ifdef SEM_WAKEUP
854 			sem_wakeup((caddr_t)semaptr);
855 #else
856 			wakeup((caddr_t)semaptr);
857 #endif
858 #ifdef SEM_DEBUG
859 			printf("semexit:  back from wakeup\n");
860 #endif
861 		}
862 	}
863 
864 	/*
865 	 * Deallocate the undo vector.
866 	 */
867 #ifdef SEM_DEBUG
868 	printf("removing vector\n");
869 #endif
870 	suptr->un_proc = NULL;
871 	*supptr = suptr->un_next;
872 }
873