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