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