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