Home | History | Annotate | Line # | Download | only in kern
sysv_sem.c revision 1.7
      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.7 1994/05/25 02:14:29 hpeyerl 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 (cred->cr_uid != 0 &&
    328 		    semaptr->sem_perm.cuid != cred->cr_uid &&
    329 		    semaptr->sem_perm.uid != cred->cr_uid)
    330 			return(EPERM);
    331 		semaptr->sem_perm.cuid = cred->cr_uid;
    332 		semaptr->sem_perm.uid = cred->cr_uid;
    333 		semtot -= semaptr->sem_nsems;
    334 		for (i = semaptr->sem_base - sem; i < semtot; i++)
    335 			sem[i] = sem[i + semaptr->sem_nsems];
    336 		for (i = 0; i < seminfo.semmni; i++) {
    337 			if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
    338 			    sema[i].sem_base > semaptr->sem_base)
    339 				sema[i].sem_base -= semaptr->sem_nsems;
    340 		}
    341 		semaptr->sem_perm.mode = 0;
    342 		semundo_clear(semid, -1);
    343 		wakeup((caddr_t)semaptr);
    344 		break;
    345 
    346 	case IPC_SET:
    347 		if (cred->cr_uid != 0 &&
    348 		    semaptr->sem_perm.cuid != cred->cr_uid &&
    349 		    semaptr->sem_perm.uid != cred->cr_uid)
    350 			return(EPERM);
    351 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
    352 			return(eval);
    353 		if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf,
    354 		    sizeof(sbuf))) != 0)
    355 			return(eval);
    356 		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
    357 		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
    358 		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
    359 		    (sbuf.sem_perm.mode & 0777);
    360 		semaptr->sem_ctime = time.tv_sec;
    361 		break;
    362 
    363 	case IPC_STAT:
    364 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
    365 			return(eval);
    366 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
    367 			return(eval);
    368 		eval = copyout((caddr_t)semaptr, real_arg.buf,
    369 		    sizeof(struct semid_ds));
    370 		break;
    371 
    372 	case GETNCNT:
    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].semncnt;
    378 		break;
    379 
    380 	case GETPID:
    381 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
    382 			return(eval);
    383 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
    384 			return(EINVAL);
    385 		rval = semaptr->sem_base[semnum].sempid;
    386 		break;
    387 
    388 	case GETVAL:
    389 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
    390 			return(eval);
    391 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
    392 			return(EINVAL);
    393 		rval = semaptr->sem_base[semnum].semval;
    394 		break;
    395 
    396 	case GETALL:
    397 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
    398 			return(eval);
    399 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
    400 			return(eval);
    401 		for (i = 0; i < semaptr->sem_nsems; i++) {
    402 			eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
    403 			    &real_arg.array[i], sizeof(real_arg.array[0]));
    404 			if (eval != 0)
    405 				break;
    406 		}
    407 		break;
    408 
    409 	case GETZCNT:
    410 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
    411 			return(eval);
    412 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
    413 			return(EINVAL);
    414 		rval = semaptr->sem_base[semnum].semzcnt;
    415 		break;
    416 
    417 	case SETVAL:
    418 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
    419 			return(eval);
    420 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
    421 			return(EINVAL);
    422 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
    423 			return(eval);
    424 		semaptr->sem_base[semnum].semval = real_arg.val;
    425 		semundo_clear(semid, semnum);
    426 		wakeup((caddr_t)semaptr);
    427 		break;
    428 
    429 	case SETALL:
    430 		if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
    431 			return(eval);
    432 		if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
    433 			return(eval);
    434 		for (i = 0; i < semaptr->sem_nsems; i++) {
    435 			eval = copyin(&real_arg.array[i],
    436 			    (caddr_t)&semaptr->sem_base[i].semval,
    437 			    sizeof(real_arg.array[0]));
    438 			if (eval != 0)
    439 				break;
    440 		}
    441 		semundo_clear(semid, -1);
    442 		wakeup((caddr_t)semaptr);
    443 		break;
    444 
    445 	default:
    446 		return(EINVAL);
    447 	}
    448 
    449 	if (eval == 0)
    450 		*retval = rval;
    451 	return(eval);
    452 }
    453 
    454 struct semget_args {
    455 	key_t	key;
    456 	int	nsems;
    457 	int	semflg;
    458 };
    459 
    460 int
    461 semget(p, uap, retval)
    462 	struct proc *p;
    463 	register struct semget_args *uap;
    464 	int *retval;
    465 {
    466 	int semid, eval;
    467 	int key = uap->key;
    468 	int nsems = uap->nsems;
    469 	int semflg = uap->semflg;
    470 	struct ucred *cred = p->p_ucred;
    471 
    472 #ifdef SEM_DEBUG
    473 	printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
    474 #endif
    475 
    476 	if (key != IPC_PRIVATE) {
    477 		for (semid = 0; semid < seminfo.semmni; semid++) {
    478 			if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
    479 			    sema[semid].sem_perm.key == key)
    480 				break;
    481 		}
    482 		if (semid < seminfo.semmni) {
    483 #ifdef SEM_DEBUG
    484 			printf("found public key\n");
    485 #endif
    486 			if ((eval = ipcperm(cred, &sema[semid].sem_perm,
    487 			    semflg & 0700)))
    488 				return(eval);
    489 			if (nsems > 0 && sema[semid].sem_nsems < nsems) {
    490 #ifdef SEM_DEBUG
    491 				printf("too small\n");
    492 #endif
    493 				return(EINVAL);
    494 			}
    495 			if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
    496 #ifdef SEM_DEBUG
    497 				printf("not exclusive\n");
    498 #endif
    499 				return(EEXIST);
    500 			}
    501 			goto found;
    502 		}
    503 	}
    504 
    505 #ifdef SEM_DEBUG
    506 	printf("need to allocate the semid_ds\n");
    507 #endif
    508 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
    509 		if (nsems <= 0 || nsems > seminfo.semmsl) {
    510 #ifdef SEM_DEBUG
    511 			printf("nsems out of range (0<%d<=%d)\n", nsems,
    512 			    seminfo.semmsl);
    513 #endif
    514 			return(EINVAL);
    515 		}
    516 		if (nsems > seminfo.semmns - semtot) {
    517 #ifdef SEM_DEBUG
    518 			printf("not enough semaphores left (need %d, got %d)\n",
    519 			    nsems, seminfo.semmns - semtot);
    520 #endif
    521 			return(ENOSPC);
    522 		}
    523 		for (semid = 0; semid < seminfo.semmni; semid++) {
    524 			if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
    525 				break;
    526 		}
    527 		if (semid == seminfo.semmni) {
    528 #ifdef SEM_DEBUG
    529 			printf("no more semid_ds's available\n");
    530 #endif
    531 			return(ENOSPC);
    532 		}
    533 #ifdef SEM_DEBUG
    534 		printf("semid %d is available\n", semid);
    535 #endif
    536 		sema[semid].sem_perm.key = key;
    537 		sema[semid].sem_perm.cuid = cred->cr_uid;
    538 		sema[semid].sem_perm.uid = cred->cr_uid;
    539 		sema[semid].sem_perm.cgid = cred->cr_gid;
    540 		sema[semid].sem_perm.gid = cred->cr_gid;
    541 		sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
    542 		sema[semid].sem_perm.seq =
    543 		    (sema[semid].sem_perm.seq + 1) & 0x7fff;
    544 		sema[semid].sem_nsems = nsems;
    545 		sema[semid].sem_otime = 0;
    546 		sema[semid].sem_ctime = time.tv_sec;
    547 		sema[semid].sem_base = &sem[semtot];
    548 		semtot += nsems;
    549 		bzero(sema[semid].sem_base,
    550 		    sizeof(sema[semid].sem_base[0])*nsems);
    551 #ifdef SEM_DEBUG
    552 		printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
    553 		    &sem[semtot]);
    554 #endif
    555 	} else {
    556 #ifdef SEM_DEBUG
    557 		printf("didn't find it and wasn't asked to create it\n");
    558 #endif
    559 		return(ENOENT);
    560 	}
    561 
    562 found:
    563 	*retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
    564 	return(0);
    565 }
    566 
    567 struct semop_args {
    568 	int	semid;
    569 	struct	sembuf *sops;
    570 	int	nsops;
    571 };
    572 
    573 int
    574 semop(p, uap, retval)
    575 	struct proc *p;
    576 	register struct semop_args *uap;
    577 	int *retval;
    578 {
    579 	int semid = uap->semid;
    580 	int nsops = uap->nsops;
    581 	struct sembuf sops[MAX_SOPS];
    582 	register struct semid_ds *semaptr;
    583 	register struct sembuf *sopptr;
    584 	register struct sem *semptr;
    585 	struct sem_undo *suptr = NULL;
    586 	struct ucred *cred = p->p_ucred;
    587 	int i, j, eval;
    588 	int all_ok, do_wakeup, do_undos;
    589 
    590 #ifdef SEM_DEBUG
    591 	printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
    592 #endif
    593 
    594 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
    595 
    596 	if (semid < 0 || semid >= seminfo.semmsl)
    597 		return(EINVAL);
    598 
    599 	semaptr = &sema[semid];
    600 	if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
    601 		return(EINVAL);
    602 	if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
    603 		return(EINVAL);
    604 
    605 	if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
    606 #ifdef SEM_DEBUG
    607 		printf("eval = %d from ipaccess\n", eval);
    608 #endif
    609 		return(eval);
    610 	}
    611 
    612 	if (nsops > MAX_SOPS) {
    613 #ifdef SEM_DEBUG
    614 		printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
    615 #endif
    616 		return(E2BIG);
    617 	}
    618 
    619 	if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
    620 #ifdef SEM_DEBUG
    621 		printf("eval = %d from copyin(%08x, %08x, %d)\n", eval,
    622 		    uap->sops, &sops, nsops * sizeof(sops[0]));
    623 #endif
    624 		return(eval);
    625 	}
    626 
    627 	/*
    628 	 * Loop trying to satisfy the vector of requests.
    629 	 * If we reach a point where we must wait, any requests already
    630 	 * performed are rolled back and we go to sleep until some other
    631 	 * process wakes us up.  At this point, we start all over again.
    632 	 *
    633 	 * This ensures that from the perspective of other tasks, a set
    634 	 * of requests is atomic (never partially satisfied).
    635 	 */
    636 	do_undos = 0;
    637 
    638 	for (;;) {
    639 		do_wakeup = 0;
    640 
    641 		for (i = 0; i < nsops; i++) {
    642 			sopptr = &sops[i];
    643 
    644 			if (sopptr->sem_num >= semaptr->sem_nsems)
    645 				return(EFBIG);
    646 
    647 			semptr = &semaptr->sem_base[sopptr->sem_num];
    648 
    649 #ifdef SEM_DEBUG
    650 			printf("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
    651 			    semaptr, semaptr->sem_base, semptr,
    652 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
    653 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
    654 #endif
    655 
    656 			if (sopptr->sem_op < 0) {
    657 				if (semptr->semval + sopptr->sem_op < 0) {
    658 #ifdef SEM_DEBUG
    659 					printf("semop:  can't do it now\n");
    660 #endif
    661 					break;
    662 				} else {
    663 					semptr->semval += sopptr->sem_op;
    664 					if (semptr->semval == 0 &&
    665 					    semptr->semzcnt > 0)
    666 						do_wakeup = 1;
    667 				}
    668 				if (sopptr->sem_flg & SEM_UNDO)
    669 					do_undos = 1;
    670 			} else if (sopptr->sem_op == 0) {
    671 				if (semptr->semval > 0) {
    672 #ifdef SEM_DEBUG
    673 					printf("semop:  not zero now\n");
    674 #endif
    675 					break;
    676 				}
    677 			} else {
    678 				if (semptr->semncnt > 0)
    679 					do_wakeup = 1;
    680 				semptr->semval += sopptr->sem_op;
    681 				if (sopptr->sem_flg & SEM_UNDO)
    682 					do_undos = 1;
    683 			}
    684 		}
    685 
    686 		/*
    687 		 * Did we get through the entire vector?
    688 		 */
    689 		if (i >= nsops)
    690 			goto done;
    691 
    692 		/*
    693 		 * No ... rollback anything that we've already done
    694 		 */
    695 #ifdef SEM_DEBUG
    696 		printf("semop:  rollback 0 through %d\n", i-1);
    697 #endif
    698 		for (j = 0; j < i; j++)
    699 			semaptr->sem_base[sops[j].sem_num].semval -=
    700 			    sops[j].sem_op;
    701 
    702 		/*
    703 		 * If the request that we couldn't satisfy has the
    704 		 * NOWAIT flag set then return with EAGAIN.
    705 		 */
    706 		if (sopptr->sem_flg & IPC_NOWAIT)
    707 			return(EAGAIN);
    708 
    709 		if (sopptr->sem_op == 0)
    710 			semptr->semzcnt++;
    711 		else
    712 			semptr->semncnt++;
    713 
    714 #ifdef SEM_DEBUG
    715 		printf("semop:  good night!\n");
    716 #endif
    717 		eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
    718 		    "semwait", 0);
    719 #ifdef SEM_DEBUG
    720 		printf("semop:  good morning (eval=%d)!\n", eval);
    721 #endif
    722 
    723 		suptr = NULL;	/* sem_undo may have been reallocated */
    724 
    725 		if (eval != 0)
    726 			return(EINTR);
    727 #ifdef SEM_DEBUG
    728 		printf("semop:  good morning!\n");
    729 #endif
    730 
    731 		/*
    732 		 * Make sure that the semaphore still exists
    733 		 */
    734 		if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
    735 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
    736 			/* The man page says to return EIDRM. */
    737 			/* Unfortunately, BSD doesn't define that code! */
    738 #ifdef EIDRM
    739 			return(EIDRM);
    740 #else
    741 			return(EINVAL);
    742 #endif
    743 		}
    744 
    745 		/*
    746 		 * The semaphore is still alive.  Readjust the count of
    747 		 * waiting processes.
    748 		 */
    749 		if (sopptr->sem_op == 0)
    750 			semptr->semzcnt--;
    751 		else
    752 			semptr->semncnt--;
    753 	}
    754 
    755 done:
    756 	/*
    757 	 * Process any SEM_UNDO requests.
    758 	 */
    759 	if (do_undos) {
    760 		for (i = 0; i < nsops; i++) {
    761 			/*
    762 			 * We only need to deal with SEM_UNDO's for non-zero
    763 			 * op's.
    764 			 */
    765 			int adjval;
    766 
    767 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
    768 				continue;
    769 			adjval = sops[i].sem_op;
    770 			if (adjval == 0)
    771 				continue;
    772 			eval = semundo_adjust(p, &suptr, semid,
    773 			    sops[i].sem_num, -adjval);
    774 			if (eval == 0)
    775 				continue;
    776 
    777 			/*
    778 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
    779 			 * Rollback the adjustments to this point and then
    780 			 * rollback the semaphore ups and down so we can return
    781 			 * with an error with all structures restored.  We
    782 			 * rollback the undo's in the exact reverse order that
    783 			 * we applied them.  This guarantees that we won't run
    784 			 * out of space as we roll things back out.
    785 			 */
    786 			for (j = i - 1; j >= 0; j--) {
    787 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
    788 					continue;
    789 				adjval = sops[j].sem_op;
    790 				if (adjval == 0)
    791 					continue;
    792 				if (semundo_adjust(p, &suptr, semid,
    793 				    sops[j].sem_num, adjval) != 0)
    794 					panic("semop - can't undo undos");
    795 			}
    796 
    797 			for (j = 0; j < nsops; j++)
    798 				semaptr->sem_base[sops[j].sem_num].semval -=
    799 				    sops[j].sem_op;
    800 
    801 #ifdef SEM_DEBUG
    802 			printf("eval = %d from semundo_adjust\n", eval);
    803 #endif
    804 			return(eval);
    805 		} /* loop through the sops */
    806 	} /* if (do_undos) */
    807 
    808 	/* We're definitely done - set the sempid's */
    809 	for (i = 0; i < nsops; i++) {
    810 		sopptr = &sops[i];
    811 		semptr = &semaptr->sem_base[sopptr->sem_num];
    812 		semptr->sempid = p->p_pid;
    813 	}
    814 
    815 	/* Do a wakeup if any semaphore was up'd. */
    816 	if (do_wakeup) {
    817 #ifdef SEM_DEBUG
    818 		printf("semop:  doing wakeup\n");
    819 #ifdef SEM_WAKEUP
    820 		sem_wakeup((caddr_t)semaptr);
    821 #else
    822 		wakeup((caddr_t)semaptr);
    823 #endif
    824 		printf("semop:  back from wakeup\n");
    825 #else
    826 		wakeup((caddr_t)semaptr);
    827 #endif
    828 	}
    829 #ifdef SEM_DEBUG
    830 	printf("semop:  done\n");
    831 #endif
    832 	*retval = 0;
    833 	return(0);
    834 }
    835 
    836 /*
    837  * Go through the undo structures for this process and apply the adjustments to
    838  * semaphores.
    839  */
    840 semexit(p)
    841 	struct proc *p;
    842 {
    843 	register struct sem_undo *suptr;
    844 	register struct sem_undo **supptr;
    845 	int did_something;
    846 
    847 	/*
    848 	 * If somebody else is holding the global semaphore facility lock
    849 	 * then sleep until it is released.
    850 	 */
    851 	while (semlock_holder != NULL && semlock_holder != p) {
    852 #ifdef SEM_DEBUG
    853 		printf("semaphore facility locked - sleeping ...\n");
    854 #endif
    855 		sleep((caddr_t)&semlock_holder, (PZERO - 4));
    856 	}
    857 
    858 	did_something = 0;
    859 
    860 	/*
    861 	 * Go through the chain of undo vectors looking for one
    862 	 * associated with this process.
    863 	 */
    864 
    865 	for (supptr = &semu_list; (suptr = *supptr) != NULL;
    866 	    supptr = &suptr->un_next) {
    867 		if (suptr->un_proc == p)
    868 			break;
    869 	}
    870 
    871 	if (suptr == NULL)
    872 		goto unlock;
    873 
    874 #ifdef SEM_DEBUG
    875 	printf("proc @%08x has undo structure with %d entries\n", p,
    876 	    suptr->un_cnt);
    877 #endif
    878 
    879 	/*
    880 	 * If there are any active undo elements then process them.
    881 	 */
    882 	if (suptr->un_cnt > 0) {
    883 		int ix;
    884 
    885 		for (ix = 0; ix < suptr->un_cnt; ix++) {
    886 			int semid = suptr->un_ent[ix].un_id;
    887 			int semnum = suptr->un_ent[ix].un_num;
    888 			int adjval = suptr->un_ent[ix].un_adjval;
    889 			struct semid_ds *semaptr;
    890 
    891 			semaptr = &sema[semid];
    892 			if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
    893 				panic("semexit - semid not allocated");
    894 			if (semnum >= semaptr->sem_nsems)
    895 				panic("semexit - semnum out of range");
    896 
    897 #ifdef SEM_DEBUG
    898 			printf("semexit:  %08x id=%d num=%d(adj=%d) ; sem=%d\n",
    899 			    suptr->un_proc, suptr->un_ent[ix].un_id,
    900 			    suptr->un_ent[ix].un_num,
    901 			    suptr->un_ent[ix].un_adjval,
    902 			    semaptr->sem_base[semnum].semval);
    903 #endif
    904 
    905 			if (adjval < 0) {
    906 				if (semaptr->sem_base[semnum].semval < -adjval)
    907 					semaptr->sem_base[semnum].semval = 0;
    908 				else
    909 					semaptr->sem_base[semnum].semval +=
    910 					    adjval;
    911 			} else
    912 				semaptr->sem_base[semnum].semval += adjval;
    913 
    914 #ifdef SEM_WAKEUP
    915 			sem_wakeup((caddr_t)semaptr);
    916 #else
    917 			wakeup((caddr_t)semaptr);
    918 #endif
    919 #ifdef SEM_DEBUG
    920 			printf("semexit:  back from wakeup\n");
    921 #endif
    922 		}
    923 	}
    924 
    925 	/*
    926 	 * Deallocate the undo vector.
    927 	 */
    928 #ifdef SEM_DEBUG
    929 	printf("removing vector\n");
    930 #endif
    931 	suptr->un_proc = NULL;
    932 	*supptr = suptr->un_next;
    933 
    934 unlock:
    935 	/*
    936 	 * If the exiting process is holding the global semaphore facility
    937 	 * lock then release it.
    938 	 */
    939 	if (semlock_holder == p) {
    940 		semlock_holder = NULL;
    941 		wakeup((caddr_t)&semlock_holder);
    942 	}
    943 }
    944