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