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