sysv_sem.c revision 2836
12836Sdg/* $Id: sysv_sem.c,v 1.1 1994/09/13 14:47:00 dfr Exp $ */ 22729Sdfr 32729Sdfr/* 42729Sdfr * Implementation of SVID semaphores 52729Sdfr * 62729Sdfr * Author: Daniel Boulet 72729Sdfr * 82729Sdfr * This software is provided ``AS IS'' without any warranties of any kind. 92729Sdfr */ 102729Sdfr 112729Sdfr#include <sys/param.h> 122729Sdfr#include <sys/systm.h> 132729Sdfr#include <sys/kernel.h> 142729Sdfr#include <sys/proc.h> 152729Sdfr#include <sys/sem.h> 162729Sdfr#include <sys/malloc.h> 172729Sdfr 182729Sdfrstatic int semctl(), semget(), semop(), semconfig(); 192729Sdfrint (*semcalls[])() = { semctl, semget, semop, semconfig }; 202729Sdfrint semtot = 0; 212729Sdfr 222729Sdfrstatic struct proc *semlock_holder = NULL; 232729Sdfr 242836Sdgvoid 252729Sdfrseminit() 262729Sdfr{ 272729Sdfr register int i; 282729Sdfr vm_offset_t whocares1, whocares2; 292729Sdfr 302729Sdfr if (sema == NULL) 312729Sdfr panic("sema is NULL"); 322729Sdfr if (semu == NULL) 332729Sdfr panic("semu is NULL"); 342729Sdfr 352729Sdfr for (i = 0; i < seminfo.semmni; i++) { 362729Sdfr sema[i].sem_base = 0; 372729Sdfr sema[i].sem_perm.mode = 0; 382729Sdfr } 392729Sdfr for (i = 0; i < seminfo.semmnu; i++) { 402729Sdfr register struct sem_undo *suptr = SEMU(i); 412729Sdfr suptr->un_proc = NULL; 422729Sdfr } 432729Sdfr semu_list = NULL; 442729Sdfr} 452729Sdfr 462729Sdfr/* 472729Sdfr * Entry point for all SEM calls 482729Sdfr */ 492729Sdfr 502729Sdfrstruct semsys_args { 512729Sdfr u_int which; 522729Sdfr}; 532729Sdfr 542729Sdfrint 552729Sdfrsemsys(p, uap, retval) 562729Sdfr struct proc *p; 572729Sdfr struct semsys_args *uap; 582729Sdfr int *retval; 592729Sdfr{ 602729Sdfr 612729Sdfr while (semlock_holder != NULL && semlock_holder != p) 622729Sdfr sleep((caddr_t)&semlock_holder, (PZERO - 4)); 632729Sdfr 642729Sdfr if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) 652729Sdfr return (EINVAL); 662729Sdfr return ((*semcalls[uap->which])(p, &uap[1], retval)); 672729Sdfr} 682729Sdfr 692729Sdfr/* 702729Sdfr * Lock or unlock the entire semaphore facility. 712729Sdfr * 722729Sdfr * This will probably eventually evolve into a general purpose semaphore 732729Sdfr * facility status enquiry mechanism (I don't like the "read /dev/kmem" 742729Sdfr * approach currently taken by ipcs and the amount of info that we want 752729Sdfr * to be able to extract for ipcs is probably beyond what the capability 762729Sdfr * of the getkerninfo facility. 772729Sdfr * 782729Sdfr * At the time that the current version of semconfig was written, ipcs is 792729Sdfr * the only user of the semconfig facility. It uses it to ensure that the 802729Sdfr * semaphore facility data structures remain static while it fishes around 812729Sdfr * in /dev/kmem. 822729Sdfr */ 832729Sdfr 842729Sdfrstruct semconfig_args { 852729Sdfr semconfig_ctl_t flag; 862729Sdfr}; 872729Sdfr 882729Sdfrint 892729Sdfrsemconfig(p, uap, retval) 902729Sdfr struct proc *p; 912729Sdfr struct semconfig_args *uap; 922729Sdfr int *retval; 932729Sdfr{ 942729Sdfr int eval = 0; 952729Sdfr 962729Sdfr switch (uap->flag) { 972729Sdfr case SEM_CONFIG_FREEZE: 982729Sdfr semlock_holder = p; 992729Sdfr break; 1002729Sdfr 1012729Sdfr case SEM_CONFIG_THAW: 1022729Sdfr semlock_holder = NULL; 1032729Sdfr wakeup((caddr_t)&semlock_holder); 1042729Sdfr break; 1052729Sdfr 1062729Sdfr default: 1072729Sdfr printf("semconfig: unknown flag parameter value (%d) - ignored\n", 1082729Sdfr uap->flag); 1092729Sdfr eval = EINVAL; 1102729Sdfr break; 1112729Sdfr } 1122729Sdfr 1132729Sdfr *retval = 0; 1142729Sdfr return(eval); 1152729Sdfr} 1162729Sdfr 1172729Sdfr/* 1182729Sdfr * Allocate a new sem_undo structure for a process 1192729Sdfr * (returns ptr to structure or NULL if no more room) 1202729Sdfr */ 1212729Sdfr 1222729Sdfrstruct sem_undo * 1232729Sdfrsemu_alloc(p) 1242729Sdfr struct proc *p; 1252729Sdfr{ 1262729Sdfr register int i; 1272729Sdfr register struct sem_undo *suptr; 1282729Sdfr register struct sem_undo **supptr; 1292729Sdfr int attempt; 1302729Sdfr 1312729Sdfr /* 1322729Sdfr * Try twice to allocate something. 1332729Sdfr * (we'll purge any empty structures after the first pass so 1342729Sdfr * two passes are always enough) 1352729Sdfr */ 1362729Sdfr 1372729Sdfr for (attempt = 0; attempt < 2; attempt++) { 1382729Sdfr /* 1392729Sdfr * Look for a free structure. 1402729Sdfr * Fill it in and return it if we find one. 1412729Sdfr */ 1422729Sdfr 1432729Sdfr for (i = 0; i < seminfo.semmnu; i++) { 1442729Sdfr suptr = SEMU(i); 1452729Sdfr if (suptr->un_proc == NULL) { 1462729Sdfr suptr->un_next = semu_list; 1472729Sdfr semu_list = suptr; 1482729Sdfr suptr->un_cnt = 0; 1492729Sdfr suptr->un_proc = p; 1502729Sdfr return(suptr); 1512729Sdfr } 1522729Sdfr } 1532729Sdfr 1542729Sdfr /* 1552729Sdfr * We didn't find a free one, if this is the first attempt 1562729Sdfr * then try to free some structures. 1572729Sdfr */ 1582729Sdfr 1592729Sdfr if (attempt == 0) { 1602729Sdfr /* All the structures are in use - try to free some */ 1612729Sdfr int did_something = 0; 1622729Sdfr 1632729Sdfr supptr = &semu_list; 1642729Sdfr while ((suptr = *supptr) != NULL) { 1652729Sdfr if (suptr->un_cnt == 0) { 1662729Sdfr suptr->un_proc = NULL; 1672729Sdfr *supptr = suptr->un_next; 1682729Sdfr did_something = 1; 1692729Sdfr } else 1702729Sdfr supptr = &(suptr->un_next); 1712729Sdfr } 1722729Sdfr 1732729Sdfr /* If we didn't free anything then just give-up */ 1742729Sdfr if (!did_something) 1752729Sdfr return(NULL); 1762729Sdfr } else { 1772729Sdfr /* 1782729Sdfr * The second pass failed even though we freed 1792729Sdfr * something after the first pass! 1802729Sdfr * This is IMPOSSIBLE! 1812729Sdfr */ 1822729Sdfr panic("semu_alloc - second attempt failed"); 1832729Sdfr } 1842729Sdfr } 1852836Sdg return (NULL); 1862729Sdfr} 1872729Sdfr 1882729Sdfr/* 1892729Sdfr * Adjust a particular entry for a particular proc 1902729Sdfr */ 1912729Sdfr 1922729Sdfrint 1932729Sdfrsemundo_adjust(p, supptr, semid, semnum, adjval) 1942729Sdfr register struct proc *p; 1952729Sdfr struct sem_undo **supptr; 1962729Sdfr int semid, semnum; 1972729Sdfr int adjval; 1982729Sdfr{ 1992729Sdfr register struct sem_undo *suptr; 2002729Sdfr register struct undo *sunptr; 2012729Sdfr int i; 2022729Sdfr 2032729Sdfr /* Look for and remember the sem_undo if the caller doesn't provide 2042729Sdfr it */ 2052729Sdfr 2062729Sdfr suptr = *supptr; 2072729Sdfr if (suptr == NULL) { 2082729Sdfr for (suptr = semu_list; suptr != NULL; 2092729Sdfr suptr = suptr->un_next) { 2102729Sdfr if (suptr->un_proc == p) { 2112729Sdfr *supptr = suptr; 2122729Sdfr break; 2132729Sdfr } 2142729Sdfr } 2152729Sdfr if (suptr == NULL) { 2162729Sdfr if (adjval == 0) 2172729Sdfr return(0); 2182729Sdfr suptr = semu_alloc(p); 2192729Sdfr if (suptr == NULL) 2202729Sdfr return(ENOSPC); 2212729Sdfr *supptr = suptr; 2222729Sdfr } 2232729Sdfr } 2242729Sdfr 2252729Sdfr /* 2262729Sdfr * Look for the requested entry and adjust it (delete if adjval becomes 2272729Sdfr * 0). 2282729Sdfr */ 2292729Sdfr sunptr = &suptr->un_ent[0]; 2302729Sdfr for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 2312729Sdfr if (sunptr->un_id != semid || sunptr->un_num != semnum) 2322729Sdfr continue; 2332729Sdfr if (adjval == 0) 2342729Sdfr sunptr->un_adjval = 0; 2352729Sdfr else 2362729Sdfr sunptr->un_adjval += adjval; 2372729Sdfr if (sunptr->un_adjval == 0) { 2382729Sdfr suptr->un_cnt--; 2392729Sdfr if (i < suptr->un_cnt) 2402729Sdfr suptr->un_ent[i] = 2412729Sdfr suptr->un_ent[suptr->un_cnt]; 2422729Sdfr } 2432729Sdfr return(0); 2442729Sdfr } 2452729Sdfr 2462729Sdfr /* Didn't find the right entry - create it */ 2472729Sdfr if (adjval == 0) 2482729Sdfr return(0); 2492729Sdfr if (suptr->un_cnt != SEMUME) { 2502729Sdfr sunptr = &suptr->un_ent[suptr->un_cnt]; 2512729Sdfr suptr->un_cnt++; 2522729Sdfr sunptr->un_adjval = adjval; 2532729Sdfr sunptr->un_id = semid; sunptr->un_num = semnum; 2542729Sdfr } else 2552729Sdfr return(EINVAL); 2562729Sdfr return(0); 2572729Sdfr} 2582729Sdfr 2592729Sdfrvoid 2602729Sdfrsemundo_clear(semid, semnum) 2612729Sdfr int semid, semnum; 2622729Sdfr{ 2632729Sdfr register struct sem_undo *suptr; 2642729Sdfr 2652729Sdfr for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 2662729Sdfr register struct undo *sunptr = &suptr->un_ent[0]; 2672729Sdfr register int i = 0; 2682729Sdfr 2692729Sdfr while (i < suptr->un_cnt) { 2702729Sdfr if (sunptr->un_id == semid) { 2712729Sdfr if (semnum == -1 || sunptr->un_num == semnum) { 2722729Sdfr suptr->un_cnt--; 2732729Sdfr if (i < suptr->un_cnt) { 2742729Sdfr suptr->un_ent[i] = 2752729Sdfr suptr->un_ent[suptr->un_cnt]; 2762729Sdfr continue; 2772729Sdfr } 2782729Sdfr } 2792729Sdfr if (semnum != -1) 2802729Sdfr break; 2812729Sdfr } 2822729Sdfr i++, sunptr++; 2832729Sdfr } 2842729Sdfr } 2852729Sdfr} 2862729Sdfr 2872729Sdfrstruct semctl_args { 2882729Sdfr int semid; 2892729Sdfr int semnum; 2902729Sdfr int cmd; 2912729Sdfr union semun *arg; 2922729Sdfr}; 2932729Sdfr 2942729Sdfrint 2952729Sdfrsemctl(p, uap, retval) 2962729Sdfr struct proc *p; 2972729Sdfr register struct semctl_args *uap; 2982729Sdfr int *retval; 2992729Sdfr{ 3002729Sdfr int semid = uap->semid; 3012729Sdfr int semnum = uap->semnum; 3022729Sdfr int cmd = uap->cmd; 3032729Sdfr union semun *arg = uap->arg; 3042729Sdfr union semun real_arg; 3052729Sdfr struct ucred *cred = p->p_ucred; 3062729Sdfr int i, rval, eval; 3072729Sdfr struct semid_ds sbuf; 3082729Sdfr register struct semid_ds *semaptr; 3092729Sdfr 3102729Sdfr#ifdef SEM_DEBUG 3112729Sdfr printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg); 3122729Sdfr#endif 3132729Sdfr 3142729Sdfr semid = IPCID_TO_IX(semid); 3152729Sdfr if (semid < 0 || semid >= seminfo.semmsl) 3162729Sdfr return(EINVAL); 3172729Sdfr 3182729Sdfr semaptr = &sema[semid]; 3192729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 3202729Sdfr semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) 3212729Sdfr return(EINVAL); 3222729Sdfr 3232729Sdfr eval = 0; 3242729Sdfr rval = 0; 3252729Sdfr 3262729Sdfr switch (cmd) { 3272729Sdfr case IPC_RMID: 3282729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) 3292729Sdfr return(eval); 3302729Sdfr semaptr->sem_perm.cuid = cred->cr_uid; 3312729Sdfr semaptr->sem_perm.uid = cred->cr_uid; 3322729Sdfr semtot -= semaptr->sem_nsems; 3332729Sdfr for (i = semaptr->sem_base - sem; i < semtot; i++) 3342729Sdfr sem[i] = sem[i + semaptr->sem_nsems]; 3352729Sdfr for (i = 0; i < seminfo.semmni; i++) { 3362729Sdfr if ((sema[i].sem_perm.mode & SEM_ALLOC) && 3372729Sdfr sema[i].sem_base > semaptr->sem_base) 3382729Sdfr sema[i].sem_base -= semaptr->sem_nsems; 3392729Sdfr } 3402729Sdfr semaptr->sem_perm.mode = 0; 3412729Sdfr semundo_clear(semid, -1); 3422729Sdfr wakeup((caddr_t)semaptr); 3432729Sdfr break; 3442729Sdfr 3452729Sdfr case IPC_SET: 3462729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) 3472729Sdfr return(eval); 3482729Sdfr if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 3492729Sdfr return(eval); 3502729Sdfr if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf, 3512729Sdfr sizeof(sbuf))) != 0) 3522729Sdfr return(eval); 3532729Sdfr semaptr->sem_perm.uid = sbuf.sem_perm.uid; 3542729Sdfr semaptr->sem_perm.gid = sbuf.sem_perm.gid; 3552729Sdfr semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 3562729Sdfr (sbuf.sem_perm.mode & 0777); 3572729Sdfr semaptr->sem_ctime = time.tv_sec; 3582729Sdfr break; 3592729Sdfr 3602729Sdfr case IPC_STAT: 3612729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 3622729Sdfr return(eval); 3632729Sdfr if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 3642729Sdfr return(eval); 3652729Sdfr eval = copyout((caddr_t)semaptr, real_arg.buf, 3662729Sdfr sizeof(struct semid_ds)); 3672729Sdfr break; 3682729Sdfr 3692729Sdfr case GETNCNT: 3702729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 3712729Sdfr return(eval); 3722729Sdfr if (semnum < 0 || semnum >= semaptr->sem_nsems) 3732729Sdfr return(EINVAL); 3742729Sdfr rval = semaptr->sem_base[semnum].semncnt; 3752729Sdfr break; 3762729Sdfr 3772729Sdfr case GETPID: 3782729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 3792729Sdfr return(eval); 3802729Sdfr if (semnum < 0 || semnum >= semaptr->sem_nsems) 3812729Sdfr return(EINVAL); 3822729Sdfr rval = semaptr->sem_base[semnum].sempid; 3832729Sdfr break; 3842729Sdfr 3852729Sdfr case GETVAL: 3862729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 3872729Sdfr return(eval); 3882729Sdfr if (semnum < 0 || semnum >= semaptr->sem_nsems) 3892729Sdfr return(EINVAL); 3902729Sdfr rval = semaptr->sem_base[semnum].semval; 3912729Sdfr break; 3922729Sdfr 3932729Sdfr case GETALL: 3942729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 3952729Sdfr return(eval); 3962729Sdfr if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 3972729Sdfr return(eval); 3982729Sdfr for (i = 0; i < semaptr->sem_nsems; i++) { 3992729Sdfr eval = copyout((caddr_t)&semaptr->sem_base[i].semval, 4002729Sdfr &real_arg.array[i], sizeof(real_arg.array[0])); 4012729Sdfr if (eval != 0) 4022729Sdfr break; 4032729Sdfr } 4042729Sdfr break; 4052729Sdfr 4062729Sdfr case GETZCNT: 4072729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 4082729Sdfr return(eval); 4092729Sdfr if (semnum < 0 || semnum >= semaptr->sem_nsems) 4102729Sdfr return(EINVAL); 4112729Sdfr rval = semaptr->sem_base[semnum].semzcnt; 4122729Sdfr break; 4132729Sdfr 4142729Sdfr case SETVAL: 4152729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 4162729Sdfr return(eval); 4172729Sdfr if (semnum < 0 || semnum >= semaptr->sem_nsems) 4182729Sdfr return(EINVAL); 4192729Sdfr if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 4202729Sdfr return(eval); 4212729Sdfr semaptr->sem_base[semnum].semval = real_arg.val; 4222729Sdfr semundo_clear(semid, semnum); 4232729Sdfr wakeup((caddr_t)semaptr); 4242729Sdfr break; 4252729Sdfr 4262729Sdfr case SETALL: 4272729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 4282729Sdfr return(eval); 4292729Sdfr if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 4302729Sdfr return(eval); 4312729Sdfr for (i = 0; i < semaptr->sem_nsems; i++) { 4322729Sdfr eval = copyin(&real_arg.array[i], 4332729Sdfr (caddr_t)&semaptr->sem_base[i].semval, 4342729Sdfr sizeof(real_arg.array[0])); 4352729Sdfr if (eval != 0) 4362729Sdfr break; 4372729Sdfr } 4382729Sdfr semundo_clear(semid, -1); 4392729Sdfr wakeup((caddr_t)semaptr); 4402729Sdfr break; 4412729Sdfr 4422729Sdfr default: 4432729Sdfr return(EINVAL); 4442729Sdfr } 4452729Sdfr 4462729Sdfr if (eval == 0) 4472729Sdfr *retval = rval; 4482729Sdfr return(eval); 4492729Sdfr} 4502729Sdfr 4512729Sdfrstruct semget_args { 4522729Sdfr key_t key; 4532729Sdfr int nsems; 4542729Sdfr int semflg; 4552729Sdfr}; 4562729Sdfr 4572729Sdfrint 4582729Sdfrsemget(p, uap, retval) 4592729Sdfr struct proc *p; 4602729Sdfr register struct semget_args *uap; 4612729Sdfr int *retval; 4622729Sdfr{ 4632729Sdfr int semid, eval; 4642729Sdfr int key = uap->key; 4652729Sdfr int nsems = uap->nsems; 4662729Sdfr int semflg = uap->semflg; 4672729Sdfr struct ucred *cred = p->p_ucred; 4682729Sdfr 4692729Sdfr#ifdef SEM_DEBUG 4702729Sdfr printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); 4712729Sdfr#endif 4722729Sdfr 4732729Sdfr if (key != IPC_PRIVATE) { 4742729Sdfr for (semid = 0; semid < seminfo.semmni; semid++) { 4752729Sdfr if ((sema[semid].sem_perm.mode & SEM_ALLOC) && 4762729Sdfr sema[semid].sem_perm.key == key) 4772729Sdfr break; 4782729Sdfr } 4792729Sdfr if (semid < seminfo.semmni) { 4802729Sdfr#ifdef SEM_DEBUG 4812729Sdfr printf("found public key\n"); 4822729Sdfr#endif 4832729Sdfr if ((eval = ipcperm(cred, &sema[semid].sem_perm, 4842729Sdfr semflg & 0700))) 4852729Sdfr return(eval); 4862729Sdfr if (nsems > 0 && sema[semid].sem_nsems < nsems) { 4872729Sdfr#ifdef SEM_DEBUG 4882729Sdfr printf("too small\n"); 4892729Sdfr#endif 4902729Sdfr return(EINVAL); 4912729Sdfr } 4922729Sdfr if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 4932729Sdfr#ifdef SEM_DEBUG 4942729Sdfr printf("not exclusive\n"); 4952729Sdfr#endif 4962729Sdfr return(EEXIST); 4972729Sdfr } 4982729Sdfr goto found; 4992729Sdfr } 5002729Sdfr } 5012729Sdfr 5022729Sdfr#ifdef SEM_DEBUG 5032729Sdfr printf("need to allocate the semid_ds\n"); 5042729Sdfr#endif 5052729Sdfr if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 5062729Sdfr if (nsems <= 0 || nsems > seminfo.semmsl) { 5072729Sdfr#ifdef SEM_DEBUG 5082729Sdfr printf("nsems out of range (0<%d<=%d)\n", nsems, 5092729Sdfr seminfo.semmsl); 5102729Sdfr#endif 5112729Sdfr return(EINVAL); 5122729Sdfr } 5132729Sdfr if (nsems > seminfo.semmns - semtot) { 5142729Sdfr#ifdef SEM_DEBUG 5152729Sdfr printf("not enough semaphores left (need %d, got %d)\n", 5162729Sdfr nsems, seminfo.semmns - semtot); 5172729Sdfr#endif 5182729Sdfr return(ENOSPC); 5192729Sdfr } 5202729Sdfr for (semid = 0; semid < seminfo.semmni; semid++) { 5212729Sdfr if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) 5222729Sdfr break; 5232729Sdfr } 5242729Sdfr if (semid == seminfo.semmni) { 5252729Sdfr#ifdef SEM_DEBUG 5262729Sdfr printf("no more semid_ds's available\n"); 5272729Sdfr#endif 5282729Sdfr return(ENOSPC); 5292729Sdfr } 5302729Sdfr#ifdef SEM_DEBUG 5312729Sdfr printf("semid %d is available\n", semid); 5322729Sdfr#endif 5332729Sdfr sema[semid].sem_perm.key = key; 5342729Sdfr sema[semid].sem_perm.cuid = cred->cr_uid; 5352729Sdfr sema[semid].sem_perm.uid = cred->cr_uid; 5362729Sdfr sema[semid].sem_perm.cgid = cred->cr_gid; 5372729Sdfr sema[semid].sem_perm.gid = cred->cr_gid; 5382729Sdfr sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; 5392729Sdfr sema[semid].sem_perm.seq = 5402729Sdfr (sema[semid].sem_perm.seq + 1) & 0x7fff; 5412729Sdfr sema[semid].sem_nsems = nsems; 5422729Sdfr sema[semid].sem_otime = 0; 5432729Sdfr sema[semid].sem_ctime = time.tv_sec; 5442729Sdfr sema[semid].sem_base = &sem[semtot]; 5452729Sdfr semtot += nsems; 5462729Sdfr bzero(sema[semid].sem_base, 5472729Sdfr sizeof(sema[semid].sem_base[0])*nsems); 5482729Sdfr#ifdef SEM_DEBUG 5492729Sdfr printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, 5502729Sdfr &sem[semtot]); 5512729Sdfr#endif 5522729Sdfr } else { 5532729Sdfr#ifdef SEM_DEBUG 5542729Sdfr printf("didn't find it and wasn't asked to create it\n"); 5552729Sdfr#endif 5562729Sdfr return(ENOENT); 5572729Sdfr } 5582729Sdfr 5592729Sdfrfound: 5602729Sdfr *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); 5612729Sdfr return(0); 5622729Sdfr} 5632729Sdfr 5642729Sdfrstruct semop_args { 5652729Sdfr int semid; 5662729Sdfr struct sembuf *sops; 5672729Sdfr int nsops; 5682729Sdfr}; 5692729Sdfr 5702729Sdfrint 5712729Sdfrsemop(p, uap, retval) 5722729Sdfr struct proc *p; 5732729Sdfr register struct semop_args *uap; 5742729Sdfr int *retval; 5752729Sdfr{ 5762729Sdfr int semid = uap->semid; 5772729Sdfr int nsops = uap->nsops; 5782729Sdfr struct sembuf sops[MAX_SOPS]; 5792729Sdfr register struct semid_ds *semaptr; 5802729Sdfr register struct sembuf *sopptr; 5812729Sdfr register struct sem *semptr; 5822729Sdfr struct sem_undo *suptr = NULL; 5832729Sdfr struct ucred *cred = p->p_ucred; 5842729Sdfr int i, j, eval; 5852729Sdfr int all_ok, do_wakeup, do_undos; 5862729Sdfr 5872729Sdfr#ifdef SEM_DEBUG 5882729Sdfr printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops); 5892729Sdfr#endif 5902729Sdfr 5912729Sdfr semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 5922729Sdfr 5932729Sdfr if (semid < 0 || semid >= seminfo.semmsl) 5942729Sdfr return(EINVAL); 5952729Sdfr 5962729Sdfr semaptr = &sema[semid]; 5972729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 5982729Sdfr return(EINVAL); 5992729Sdfr if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) 6002729Sdfr return(EINVAL); 6012729Sdfr 6022729Sdfr if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { 6032729Sdfr#ifdef SEM_DEBUG 6042729Sdfr printf("eval = %d from ipaccess\n", eval); 6052729Sdfr#endif 6062729Sdfr return(eval); 6072729Sdfr } 6082729Sdfr 6092729Sdfr if (nsops > MAX_SOPS) { 6102729Sdfr#ifdef SEM_DEBUG 6112729Sdfr printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); 6122729Sdfr#endif 6132729Sdfr return(E2BIG); 6142729Sdfr } 6152729Sdfr 6162729Sdfr if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) { 6172729Sdfr#ifdef SEM_DEBUG 6182729Sdfr printf("eval = %d from copyin(%08x, %08x, %d)\n", eval, 6192729Sdfr uap->sops, &sops, nsops * sizeof(sops[0])); 6202729Sdfr#endif 6212729Sdfr return(eval); 6222729Sdfr } 6232729Sdfr 6242729Sdfr /* 6252729Sdfr * Loop trying to satisfy the vector of requests. 6262729Sdfr * If we reach a point where we must wait, any requests already 6272729Sdfr * performed are rolled back and we go to sleep until some other 6282729Sdfr * process wakes us up. At this point, we start all over again. 6292729Sdfr * 6302729Sdfr * This ensures that from the perspective of other tasks, a set 6312729Sdfr * of requests is atomic (never partially satisfied). 6322729Sdfr */ 6332729Sdfr do_undos = 0; 6342729Sdfr 6352729Sdfr for (;;) { 6362729Sdfr do_wakeup = 0; 6372729Sdfr 6382729Sdfr for (i = 0; i < nsops; i++) { 6392729Sdfr sopptr = &sops[i]; 6402729Sdfr 6412729Sdfr if (sopptr->sem_num >= semaptr->sem_nsems) 6422729Sdfr return(EFBIG); 6432729Sdfr 6442729Sdfr semptr = &semaptr->sem_base[sopptr->sem_num]; 6452729Sdfr 6462729Sdfr#ifdef SEM_DEBUG 6472729Sdfr printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 6482729Sdfr semaptr, semaptr->sem_base, semptr, 6492729Sdfr sopptr->sem_num, semptr->semval, sopptr->sem_op, 6502729Sdfr (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); 6512729Sdfr#endif 6522729Sdfr 6532729Sdfr if (sopptr->sem_op < 0) { 6542729Sdfr if (semptr->semval + sopptr->sem_op < 0) { 6552729Sdfr#ifdef SEM_DEBUG 6562729Sdfr printf("semop: can't do it now\n"); 6572729Sdfr#endif 6582729Sdfr break; 6592729Sdfr } else { 6602729Sdfr semptr->semval += sopptr->sem_op; 6612729Sdfr if (semptr->semval == 0 && 6622729Sdfr semptr->semzcnt > 0) 6632729Sdfr do_wakeup = 1; 6642729Sdfr } 6652729Sdfr if (sopptr->sem_flg & SEM_UNDO) 6662729Sdfr do_undos = 1; 6672729Sdfr } else if (sopptr->sem_op == 0) { 6682729Sdfr if (semptr->semval > 0) { 6692729Sdfr#ifdef SEM_DEBUG 6702729Sdfr printf("semop: not zero now\n"); 6712729Sdfr#endif 6722729Sdfr break; 6732729Sdfr } 6742729Sdfr } else { 6752729Sdfr if (semptr->semncnt > 0) 6762729Sdfr do_wakeup = 1; 6772729Sdfr semptr->semval += sopptr->sem_op; 6782729Sdfr if (sopptr->sem_flg & SEM_UNDO) 6792729Sdfr do_undos = 1; 6802729Sdfr } 6812729Sdfr } 6822729Sdfr 6832729Sdfr /* 6842729Sdfr * Did we get through the entire vector? 6852729Sdfr */ 6862729Sdfr if (i >= nsops) 6872729Sdfr goto done; 6882729Sdfr 6892729Sdfr /* 6902729Sdfr * No ... rollback anything that we've already done 6912729Sdfr */ 6922729Sdfr#ifdef SEM_DEBUG 6932729Sdfr printf("semop: rollback 0 through %d\n", i-1); 6942729Sdfr#endif 6952729Sdfr for (j = 0; j < i; j++) 6962729Sdfr semaptr->sem_base[sops[j].sem_num].semval -= 6972729Sdfr sops[j].sem_op; 6982729Sdfr 6992729Sdfr /* 7002729Sdfr * If the request that we couldn't satisfy has the 7012729Sdfr * NOWAIT flag set then return with EAGAIN. 7022729Sdfr */ 7032729Sdfr if (sopptr->sem_flg & IPC_NOWAIT) 7042729Sdfr return(EAGAIN); 7052729Sdfr 7062729Sdfr if (sopptr->sem_op == 0) 7072729Sdfr semptr->semzcnt++; 7082729Sdfr else 7092729Sdfr semptr->semncnt++; 7102729Sdfr 7112729Sdfr#ifdef SEM_DEBUG 7122729Sdfr printf("semop: good night!\n"); 7132729Sdfr#endif 7142729Sdfr eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, 7152729Sdfr "semwait", 0); 7162729Sdfr#ifdef SEM_DEBUG 7172729Sdfr printf("semop: good morning (eval=%d)!\n", eval); 7182729Sdfr#endif 7192729Sdfr 7202729Sdfr suptr = NULL; /* sem_undo may have been reallocated */ 7212729Sdfr 7222729Sdfr if (eval != 0) 7232729Sdfr return(EINTR); 7242729Sdfr#ifdef SEM_DEBUG 7252729Sdfr printf("semop: good morning!\n"); 7262729Sdfr#endif 7272729Sdfr 7282729Sdfr /* 7292729Sdfr * Make sure that the semaphore still exists 7302729Sdfr */ 7312729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 7322729Sdfr semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 7332729Sdfr /* The man page says to return EIDRM. */ 7342729Sdfr /* Unfortunately, BSD doesn't define that code! */ 7352729Sdfr#ifdef EIDRM 7362729Sdfr return(EIDRM); 7372729Sdfr#else 7382729Sdfr return(EINVAL); 7392729Sdfr#endif 7402729Sdfr } 7412729Sdfr 7422729Sdfr /* 7432729Sdfr * The semaphore is still alive. Readjust the count of 7442729Sdfr * waiting processes. 7452729Sdfr */ 7462729Sdfr if (sopptr->sem_op == 0) 7472729Sdfr semptr->semzcnt--; 7482729Sdfr else 7492729Sdfr semptr->semncnt--; 7502729Sdfr } 7512729Sdfr 7522729Sdfrdone: 7532729Sdfr /* 7542729Sdfr * Process any SEM_UNDO requests. 7552729Sdfr */ 7562729Sdfr if (do_undos) { 7572729Sdfr for (i = 0; i < nsops; i++) { 7582729Sdfr /* 7592729Sdfr * We only need to deal with SEM_UNDO's for non-zero 7602729Sdfr * op's. 7612729Sdfr */ 7622729Sdfr int adjval; 7632729Sdfr 7642729Sdfr if ((sops[i].sem_flg & SEM_UNDO) == 0) 7652729Sdfr continue; 7662729Sdfr adjval = sops[i].sem_op; 7672729Sdfr if (adjval == 0) 7682729Sdfr continue; 7692729Sdfr eval = semundo_adjust(p, &suptr, semid, 7702729Sdfr sops[i].sem_num, -adjval); 7712729Sdfr if (eval == 0) 7722729Sdfr continue; 7732729Sdfr 7742729Sdfr /* 7752729Sdfr * Oh-Oh! We ran out of either sem_undo's or undo's. 7762729Sdfr * Rollback the adjustments to this point and then 7772729Sdfr * rollback the semaphore ups and down so we can return 7782729Sdfr * with an error with all structures restored. We 7792729Sdfr * rollback the undo's in the exact reverse order that 7802729Sdfr * we applied them. This guarantees that we won't run 7812729Sdfr * out of space as we roll things back out. 7822729Sdfr */ 7832729Sdfr for (j = i - 1; j >= 0; j--) { 7842729Sdfr if ((sops[j].sem_flg & SEM_UNDO) == 0) 7852729Sdfr continue; 7862729Sdfr adjval = sops[j].sem_op; 7872729Sdfr if (adjval == 0) 7882729Sdfr continue; 7892729Sdfr if (semundo_adjust(p, &suptr, semid, 7902729Sdfr sops[j].sem_num, adjval) != 0) 7912729Sdfr panic("semop - can't undo undos"); 7922729Sdfr } 7932729Sdfr 7942729Sdfr for (j = 0; j < nsops; j++) 7952729Sdfr semaptr->sem_base[sops[j].sem_num].semval -= 7962729Sdfr sops[j].sem_op; 7972729Sdfr 7982729Sdfr#ifdef SEM_DEBUG 7992729Sdfr printf("eval = %d from semundo_adjust\n", eval); 8002729Sdfr#endif 8012729Sdfr return(eval); 8022729Sdfr } /* loop through the sops */ 8032729Sdfr } /* if (do_undos) */ 8042729Sdfr 8052729Sdfr /* We're definitely done - set the sempid's */ 8062729Sdfr for (i = 0; i < nsops; i++) { 8072729Sdfr sopptr = &sops[i]; 8082729Sdfr semptr = &semaptr->sem_base[sopptr->sem_num]; 8092729Sdfr semptr->sempid = p->p_pid; 8102729Sdfr } 8112729Sdfr 8122729Sdfr /* Do a wakeup if any semaphore was up'd. */ 8132729Sdfr if (do_wakeup) { 8142729Sdfr#ifdef SEM_DEBUG 8152729Sdfr printf("semop: doing wakeup\n"); 8162729Sdfr#ifdef SEM_WAKEUP 8172729Sdfr sem_wakeup((caddr_t)semaptr); 8182729Sdfr#else 8192729Sdfr wakeup((caddr_t)semaptr); 8202729Sdfr#endif 8212729Sdfr printf("semop: back from wakeup\n"); 8222729Sdfr#else 8232729Sdfr wakeup((caddr_t)semaptr); 8242729Sdfr#endif 8252729Sdfr } 8262729Sdfr#ifdef SEM_DEBUG 8272729Sdfr printf("semop: done\n"); 8282729Sdfr#endif 8292729Sdfr *retval = 0; 8302729Sdfr return(0); 8312729Sdfr} 8322729Sdfr 8332729Sdfr/* 8342729Sdfr * Go through the undo structures for this process and apply the adjustments to 8352729Sdfr * semaphores. 8362729Sdfr */ 8372836Sdgvoid 8382729Sdfrsemexit(p) 8392729Sdfr struct proc *p; 8402729Sdfr{ 8412729Sdfr register struct sem_undo *suptr; 8422729Sdfr register struct sem_undo **supptr; 8432729Sdfr int did_something; 8442729Sdfr 8452729Sdfr /* 8462729Sdfr * If somebody else is holding the global semaphore facility lock 8472729Sdfr * then sleep until it is released. 8482729Sdfr */ 8492729Sdfr while (semlock_holder != NULL && semlock_holder != p) { 8502729Sdfr#ifdef SEM_DEBUG 8512729Sdfr printf("semaphore facility locked - sleeping ...\n"); 8522729Sdfr#endif 8532729Sdfr sleep((caddr_t)&semlock_holder, (PZERO - 4)); 8542729Sdfr } 8552729Sdfr 8562729Sdfr did_something = 0; 8572729Sdfr 8582729Sdfr /* 8592729Sdfr * Go through the chain of undo vectors looking for one 8602729Sdfr * associated with this process. 8612729Sdfr */ 8622729Sdfr 8632729Sdfr for (supptr = &semu_list; (suptr = *supptr) != NULL; 8642729Sdfr supptr = &suptr->un_next) { 8652729Sdfr if (suptr->un_proc == p) 8662729Sdfr break; 8672729Sdfr } 8682729Sdfr 8692729Sdfr if (suptr == NULL) 8702729Sdfr goto unlock; 8712729Sdfr 8722729Sdfr#ifdef SEM_DEBUG 8732729Sdfr printf("proc @%08x has undo structure with %d entries\n", p, 8742729Sdfr suptr->un_cnt); 8752729Sdfr#endif 8762729Sdfr 8772729Sdfr /* 8782729Sdfr * If there are any active undo elements then process them. 8792729Sdfr */ 8802729Sdfr if (suptr->un_cnt > 0) { 8812729Sdfr int ix; 8822729Sdfr 8832729Sdfr for (ix = 0; ix < suptr->un_cnt; ix++) { 8842729Sdfr int semid = suptr->un_ent[ix].un_id; 8852729Sdfr int semnum = suptr->un_ent[ix].un_num; 8862729Sdfr int adjval = suptr->un_ent[ix].un_adjval; 8872729Sdfr struct semid_ds *semaptr; 8882729Sdfr 8892729Sdfr semaptr = &sema[semid]; 8902729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 8912729Sdfr panic("semexit - semid not allocated"); 8922729Sdfr if (semnum >= semaptr->sem_nsems) 8932729Sdfr panic("semexit - semnum out of range"); 8942729Sdfr 8952729Sdfr#ifdef SEM_DEBUG 8962729Sdfr printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", 8972729Sdfr suptr->un_proc, suptr->un_ent[ix].un_id, 8982729Sdfr suptr->un_ent[ix].un_num, 8992729Sdfr suptr->un_ent[ix].un_adjval, 9002729Sdfr semaptr->sem_base[semnum].semval); 9012729Sdfr#endif 9022729Sdfr 9032729Sdfr if (adjval < 0) { 9042729Sdfr if (semaptr->sem_base[semnum].semval < -adjval) 9052729Sdfr semaptr->sem_base[semnum].semval = 0; 9062729Sdfr else 9072729Sdfr semaptr->sem_base[semnum].semval += 9082729Sdfr adjval; 9092729Sdfr } else 9102729Sdfr semaptr->sem_base[semnum].semval += adjval; 9112729Sdfr 9122729Sdfr#ifdef SEM_WAKEUP 9132729Sdfr sem_wakeup((caddr_t)semaptr); 9142729Sdfr#else 9152729Sdfr wakeup((caddr_t)semaptr); 9162729Sdfr#endif 9172729Sdfr#ifdef SEM_DEBUG 9182729Sdfr printf("semexit: back from wakeup\n"); 9192729Sdfr#endif 9202729Sdfr } 9212729Sdfr } 9222729Sdfr 9232729Sdfr /* 9242729Sdfr * Deallocate the undo vector. 9252729Sdfr */ 9262729Sdfr#ifdef SEM_DEBUG 9272729Sdfr printf("removing vector\n"); 9282729Sdfr#endif 9292729Sdfr suptr->un_proc = NULL; 9302729Sdfr *supptr = suptr->un_next; 9312729Sdfr 9322729Sdfrunlock: 9332729Sdfr /* 9342729Sdfr * If the exiting process is holding the global semaphore facility 9352729Sdfr * lock then release it. 9362729Sdfr */ 9372729Sdfr if (semlock_holder == p) { 9382729Sdfr semlock_holder = NULL; 9392729Sdfr wakeup((caddr_t)&semlock_holder); 9402729Sdfr } 9412729Sdfr} 942