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