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