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