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