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