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