sysv_sem.c revision 111119
150477Speter/* $FreeBSD: head/sys/kern/sysv_sem.c 111119 2003-02-19 05:47:46Z imp $ */ 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 1159839Speter#include "opt_sysvipc.h" 1259839Speter 132729Sdfr#include <sys/param.h> 142729Sdfr#include <sys/systm.h> 1511626Sbde#include <sys/sysproto.h> 162729Sdfr#include <sys/kernel.h> 172729Sdfr#include <sys/proc.h> 1882607Sdillon#include <sys/lock.h> 1982607Sdillon#include <sys/mutex.h> 202729Sdfr#include <sys/sem.h> 2169449Salfred#include <sys/syscall.h> 2211626Sbde#include <sys/sysent.h> 2359839Speter#include <sys/sysctl.h> 2459839Speter#include <sys/malloc.h> 2568024Srwatson#include <sys/jail.h> 262729Sdfr 2759839Speterstatic MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores"); 2859839Speter 29100523Salfred#ifdef SEM_DEBUG 30100523Salfred#define DPRINTF(a) printf a 31100523Salfred#else 32100523Salfred#define DPRINTF(a) 33100523Salfred#endif 34100523Salfred 3592723Salfredstatic void seminit(void); 3692723Salfredstatic int sysvsem_modload(struct module *, int, void *); 3792723Salfredstatic int semunload(void); 3892723Salfredstatic void semexit_myhook(struct proc *p); 3992723Salfredstatic int sysctl_sema(SYSCTL_HANDLER_ARGS); 40101774Salfredstatic int semvalid(int semid, struct semid_ds *semaptr); 4110358Sjulian 4212866Speter#ifndef _SYS_SYSPROTO_H_ 4312866Speterstruct __semctl_args; 4492723Salfredint __semctl(struct thread *td, struct __semctl_args *uap); 4511626Sbdestruct semget_args; 4692723Salfredint semget(struct thread *td, struct semget_args *uap); 4711626Sbdestruct semop_args; 4892723Salfredint semop(struct thread *td, struct semop_args *uap); 4912866Speter#endif 5011626Sbde 5192723Salfredstatic struct sem_undo *semu_alloc(struct thread *td); 5292723Salfredstatic int semundo_adjust(struct thread *td, struct sem_undo **supptr, 5392723Salfred int semid, int semnum, int adjval); 5492723Salfredstatic void semundo_clear(int semid, int semnum); 5511626Sbde 5611626Sbde/* XXX casting to (sy_call_t *) is bogus, as usual. */ 5712819Sphkstatic sy_call_t *semcalls[] = { 5812866Speter (sy_call_t *)__semctl, (sy_call_t *)semget, 5959828Speter (sy_call_t *)semop 6011626Sbde}; 6111626Sbde 62101774Salfredstatic struct mtx sem_mtx; /* semaphore global lock */ 6312819Sphkstatic int semtot = 0; 6459839Speterstatic struct semid_ds *sema; /* semaphore id pool */ 65101774Salfredstatic struct mtx *sema_mtx; /* semaphore id pool mutexes*/ 6659839Speterstatic struct sem *sem; /* semaphore pool */ 67101774SalfredSLIST_HEAD(, sem_undo) semu_list; /* list of active undo structures */ 6859839Speterstatic int *semu; /* undo structure pool */ 692729Sdfr 70101774Salfred#define SEMUNDO_MTX sem_mtx 71101774Salfred#define SEMUNDO_LOCK() mtx_lock(&SEMUNDO_MTX); 72101774Salfred#define SEMUNDO_UNLOCK() mtx_unlock(&SEMUNDO_MTX); 73101774Salfred#define SEMUNDO_LOCKASSERT(how) mtx_assert(&SEMUNDO_MTX, (how)); 74101774Salfred 7559839Speterstruct sem { 7659839Speter u_short semval; /* semaphore value */ 7759839Speter pid_t sempid; /* pid of last operation */ 7859839Speter u_short semncnt; /* # awaiting semval > cval */ 7959839Speter u_short semzcnt; /* # awaiting semval = 0 */ 8059839Speter}; 8159839Speter 8259839Speter/* 8359839Speter * Undo structure (one per process) 8459839Speter */ 8559839Speterstruct sem_undo { 86101774Salfred SLIST_ENTRY(sem_undo) un_next; /* ptr to next active undo structure */ 8759839Speter struct proc *un_proc; /* owner of this structure */ 8859839Speter short un_cnt; /* # of active entries */ 8959839Speter struct undo { 9059839Speter short un_adjval; /* adjust on exit values */ 9159839Speter short un_num; /* semaphore # */ 9259839Speter int un_id; /* semid */ 9359839Speter } un_ent[1]; /* undo entries */ 9459839Speter}; 9559839Speter 9659839Speter/* 9759839Speter * Configuration parameters 9859839Speter */ 9959839Speter#ifndef SEMMNI 10059839Speter#define SEMMNI 10 /* # of semaphore identifiers */ 10159839Speter#endif 10259839Speter#ifndef SEMMNS 10359839Speter#define SEMMNS 60 /* # of semaphores in system */ 10459839Speter#endif 10559839Speter#ifndef SEMUME 10659839Speter#define SEMUME 10 /* max # of undo entries per process */ 10759839Speter#endif 10859839Speter#ifndef SEMMNU 10959839Speter#define SEMMNU 30 /* # of undo structures in system */ 11059839Speter#endif 11159839Speter 11259839Speter/* shouldn't need tuning */ 11359839Speter#ifndef SEMMAP 11459839Speter#define SEMMAP 30 /* # of entries in semaphore map */ 11559839Speter#endif 11659839Speter#ifndef SEMMSL 11759839Speter#define SEMMSL SEMMNS /* max # of semaphores per id */ 11859839Speter#endif 11959839Speter#ifndef SEMOPM 12059839Speter#define SEMOPM 100 /* max # of operations per semop call */ 12159839Speter#endif 12259839Speter 12359839Speter#define SEMVMX 32767 /* semaphore maximum value */ 12459839Speter#define SEMAEM 16384 /* adjust on exit max value */ 12559839Speter 12659839Speter/* 12759839Speter * Due to the way semaphore memory is allocated, we have to ensure that 12859839Speter * SEMUSZ is properly aligned. 12959839Speter */ 13059839Speter 13159839Speter#define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1)) 13259839Speter 13359839Speter/* actual size of an undo structure */ 13459839Speter#define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME])) 13559839Speter 13659839Speter/* 13759839Speter * Macro to find a particular sem_undo vector 13859839Speter */ 139101350Salfred#define SEMU(ix) \ 140101350Salfred ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz)) 14159839Speter 14259839Speter/* 14359839Speter * semaphore info struct 14459839Speter */ 14559839Speterstruct seminfo seminfo = { 14659839Speter SEMMAP, /* # of entries in semaphore map */ 14759839Speter SEMMNI, /* # of semaphore identifiers */ 14859839Speter SEMMNS, /* # of semaphores in system */ 14959839Speter SEMMNU, /* # of undo structures in system */ 15059839Speter SEMMSL, /* max # of semaphores per id */ 15159839Speter SEMOPM, /* max # of operations per semop call */ 15259839Speter SEMUME, /* max # of undo entries per process */ 15359839Speter SEMUSZ, /* size in bytes of undo structure */ 15459839Speter SEMVMX, /* semaphore maximum value */ 15559839Speter SEMAEM /* adjust on exit max value */ 15659839Speter}; 15759839Speter 15859839SpeterSYSCTL_DECL(_kern_ipc); 15959839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, ""); 16059839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, ""); 16159839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, ""); 16259839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, ""); 16359839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, ""); 16459839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, ""); 16559839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, ""); 16659839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, ""); 16759839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, ""); 16859839SpeterSYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, ""); 16977461SddSYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD, 17077461Sdd NULL, 0, sysctl_sema, "", ""); 17159839Speter 17259839Speterstatic void 17369449Salfredseminit(void) 1742729Sdfr{ 175101350Salfred int i; 1762729Sdfr 17783413Smr TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo.semmap); 17883413Smr TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo.semmni); 17983413Smr TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo.semmns); 18083413Smr TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo.semmnu); 18183413Smr TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo.semmsl); 18283413Smr TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo.semopm); 18383413Smr TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo.semume); 18483413Smr TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo.semusz); 18583413Smr TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo.semvmx); 18683413Smr TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo.semaem); 18783413Smr 188111119Simp sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK); 189101350Salfred sema = malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM, 190111119Simp M_WAITOK); 191101774Salfred sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM, 192111119Simp M_WAITOK | M_ZERO); 193111119Simp semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); 1942729Sdfr 1952729Sdfr for (i = 0; i < seminfo.semmni; i++) { 1962729Sdfr sema[i].sem_base = 0; 1972729Sdfr sema[i].sem_perm.mode = 0; 1982729Sdfr } 199101774Salfred for (i = 0; i < seminfo.semmni; i++) 200101774Salfred mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF); 2012729Sdfr for (i = 0; i < seminfo.semmnu; i++) { 202101350Salfred struct sem_undo *suptr = SEMU(i); 2032729Sdfr suptr->un_proc = NULL; 2042729Sdfr } 205101774Salfred SLIST_INIT(&semu_list); 20688715Salc at_exit(semexit_myhook); 207101774Salfred mtx_init(&sem_mtx, "sem", NULL, MTX_DEF); 2082729Sdfr} 2092729Sdfr 21069449Salfredstatic int 21169449Salfredsemunload(void) 21269449Salfred{ 213101774Salfred int i; 21469449Salfred 21569449Salfred if (semtot != 0) 21669449Salfred return (EBUSY); 21769449Salfred 21869449Salfred free(sem, M_SEM); 21969449Salfred free(sema, M_SEM); 22069449Salfred free(semu, M_SEM); 22188715Salc rm_at_exit(semexit_myhook); 222101774Salfred for (i = 0; i < seminfo.semmni; i++) 223101774Salfred mtx_destroy(&sema_mtx[i]); 224101774Salfred mtx_destroy(&sem_mtx); 22569449Salfred return (0); 22669449Salfred} 22769449Salfred 22869449Salfredstatic int 22969449Salfredsysvsem_modload(struct module *module, int cmd, void *arg) 23069449Salfred{ 23169449Salfred int error = 0; 23269449Salfred 23369449Salfred switch (cmd) { 23469449Salfred case MOD_LOAD: 23569449Salfred seminit(); 23669449Salfred break; 23769449Salfred case MOD_UNLOAD: 23869449Salfred error = semunload(); 23969449Salfred break; 24069449Salfred case MOD_SHUTDOWN: 24169449Salfred break; 24269449Salfred default: 24369449Salfred error = EINVAL; 24469449Salfred break; 24569449Salfred } 24669449Salfred return (error); 24769449Salfred} 24869449Salfred 24971038Sdesstatic moduledata_t sysvsem_mod = { 25071038Sdes "sysvsem", 25169449Salfred &sysvsem_modload, 25269449Salfred NULL 25369449Salfred}; 25469449Salfred 25588633SalfredSYSCALL_MODULE_HELPER(semsys); 25688633SalfredSYSCALL_MODULE_HELPER(__semctl); 25788633SalfredSYSCALL_MODULE_HELPER(semget); 25888633SalfredSYSCALL_MODULE_HELPER(semop); 25969449Salfred 26071038SdesDECLARE_MODULE(sysvsem, sysvsem_mod, 26169449Salfred SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 26271038SdesMODULE_VERSION(sysvsem, 1); 26369449Salfred 2642729Sdfr/* 2652729Sdfr * Entry point for all SEM calls 26682607Sdillon * 26782607Sdillon * MPSAFE 2682729Sdfr */ 2692729Sdfrint 27083366Sjuliansemsys(td, uap) 27183366Sjulian struct thread *td; 27211626Sbde /* XXX actually varargs. */ 27311626Sbde struct semsys_args /* { 27411626Sbde u_int which; 27511626Sbde int a2; 27611626Sbde int a3; 27711626Sbde int a4; 27811626Sbde int a5; 27911626Sbde } */ *uap; 2802729Sdfr{ 28182607Sdillon int error; 2822729Sdfr 28391703Sjhb if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 28491703Sjhb return (ENOSYS); 28591703Sjhb if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) 28691703Sjhb return (EINVAL); 28783366Sjulian error = (*semcalls[uap->which])(td, &uap->a2); 28882607Sdillon return (error); 2892729Sdfr} 2902729Sdfr 2912729Sdfr/* 2922729Sdfr * Allocate a new sem_undo structure for a process 2932729Sdfr * (returns ptr to structure or NULL if no more room) 2942729Sdfr */ 2952729Sdfr 29612819Sphkstatic struct sem_undo * 29783366Sjuliansemu_alloc(td) 29883366Sjulian struct thread *td; 2992729Sdfr{ 300101350Salfred int i; 301101350Salfred struct sem_undo *suptr; 302101350Salfred struct sem_undo **supptr; 3032729Sdfr int attempt; 3042729Sdfr 305101774Salfred SEMUNDO_LOCKASSERT(MA_OWNED); 3062729Sdfr /* 3072729Sdfr * Try twice to allocate something. 3082729Sdfr * (we'll purge any empty structures after the first pass so 3092729Sdfr * two passes are always enough) 3102729Sdfr */ 3112729Sdfr 3122729Sdfr for (attempt = 0; attempt < 2; attempt++) { 3132729Sdfr /* 3142729Sdfr * Look for a free structure. 3152729Sdfr * Fill it in and return it if we find one. 3162729Sdfr */ 3172729Sdfr 3182729Sdfr for (i = 0; i < seminfo.semmnu; i++) { 3192729Sdfr suptr = SEMU(i); 3202729Sdfr if (suptr->un_proc == NULL) { 321101774Salfred SLIST_INSERT_HEAD(&semu_list, suptr, un_next); 3222729Sdfr suptr->un_cnt = 0; 32383366Sjulian suptr->un_proc = td->td_proc; 3242729Sdfr return(suptr); 3252729Sdfr } 3262729Sdfr } 3272729Sdfr 3282729Sdfr /* 3292729Sdfr * We didn't find a free one, if this is the first attempt 3302729Sdfr * then try to free some structures. 3312729Sdfr */ 3322729Sdfr 3332729Sdfr if (attempt == 0) { 3342729Sdfr /* All the structures are in use - try to free some */ 3352729Sdfr int did_something = 0; 3362729Sdfr 337101774Salfred SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, 338101774Salfred un_next) { 339101774Salfred if (suptr->un_cnt == 0) { 3402729Sdfr suptr->un_proc = NULL; 3412729Sdfr did_something = 1; 342101774Salfred *supptr = SLIST_NEXT(suptr, un_next); 343101774Salfred } 3442729Sdfr } 3452729Sdfr 3462729Sdfr /* If we didn't free anything then just give-up */ 3472729Sdfr if (!did_something) 3482729Sdfr return(NULL); 3492729Sdfr } else { 3502729Sdfr /* 3512729Sdfr * The second pass failed even though we freed 3522729Sdfr * something after the first pass! 3532729Sdfr * This is IMPOSSIBLE! 3542729Sdfr */ 3552729Sdfr panic("semu_alloc - second attempt failed"); 3562729Sdfr } 3572729Sdfr } 3582836Sdg return (NULL); 3592729Sdfr} 3602729Sdfr 3612729Sdfr/* 3622729Sdfr * Adjust a particular entry for a particular proc 3632729Sdfr */ 3642729Sdfr 36512819Sphkstatic int 36683366Sjuliansemundo_adjust(td, supptr, semid, semnum, adjval) 367101350Salfred struct thread *td; 3682729Sdfr struct sem_undo **supptr; 3692729Sdfr int semid, semnum; 3702729Sdfr int adjval; 3712729Sdfr{ 37283366Sjulian struct proc *p = td->td_proc; 373101350Salfred struct sem_undo *suptr; 374101350Salfred struct undo *sunptr; 3752729Sdfr int i; 3762729Sdfr 377101774Salfred SEMUNDO_LOCKASSERT(MA_OWNED); 3782729Sdfr /* Look for and remember the sem_undo if the caller doesn't provide 3792729Sdfr it */ 3802729Sdfr 3812729Sdfr suptr = *supptr; 3822729Sdfr if (suptr == NULL) { 383101774Salfred SLIST_FOREACH(suptr, &semu_list, un_next) { 3842729Sdfr if (suptr->un_proc == p) { 3852729Sdfr *supptr = suptr; 3862729Sdfr break; 3872729Sdfr } 3882729Sdfr } 3892729Sdfr if (suptr == NULL) { 3902729Sdfr if (adjval == 0) 3912729Sdfr return(0); 39283366Sjulian suptr = semu_alloc(td); 3932729Sdfr if (suptr == NULL) 3942729Sdfr return(ENOSPC); 3952729Sdfr *supptr = suptr; 3962729Sdfr } 3972729Sdfr } 3982729Sdfr 3992729Sdfr /* 4002729Sdfr * Look for the requested entry and adjust it (delete if adjval becomes 4012729Sdfr * 0). 4022729Sdfr */ 4032729Sdfr sunptr = &suptr->un_ent[0]; 4042729Sdfr for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 4052729Sdfr if (sunptr->un_id != semid || sunptr->un_num != semnum) 4062729Sdfr continue; 40784789Smr if (adjval != 0) { 40884789Smr adjval += sunptr->un_adjval; 40984789Smr if (adjval > seminfo.semaem || adjval < -seminfo.semaem) 41084789Smr return (ERANGE); 41184789Smr } 41284789Smr sunptr->un_adjval = adjval; 4132729Sdfr if (sunptr->un_adjval == 0) { 4142729Sdfr suptr->un_cnt--; 4152729Sdfr if (i < suptr->un_cnt) 4162729Sdfr suptr->un_ent[i] = 4172729Sdfr suptr->un_ent[suptr->un_cnt]; 4182729Sdfr } 4192729Sdfr return(0); 4202729Sdfr } 4212729Sdfr 4222729Sdfr /* Didn't find the right entry - create it */ 4232729Sdfr if (adjval == 0) 4242729Sdfr return(0); 42584789Smr if (adjval > seminfo.semaem || adjval < -seminfo.semaem) 42684789Smr return (ERANGE); 42741774Sdillon if (suptr->un_cnt != seminfo.semume) { 4282729Sdfr sunptr = &suptr->un_ent[suptr->un_cnt]; 4292729Sdfr suptr->un_cnt++; 4302729Sdfr sunptr->un_adjval = adjval; 4312729Sdfr sunptr->un_id = semid; sunptr->un_num = semnum; 4322729Sdfr } else 4332729Sdfr return(EINVAL); 4342729Sdfr return(0); 4352729Sdfr} 4362729Sdfr 43712819Sphkstatic void 4382729Sdfrsemundo_clear(semid, semnum) 4392729Sdfr int semid, semnum; 4402729Sdfr{ 441101350Salfred struct sem_undo *suptr; 4422729Sdfr 443101774Salfred SEMUNDO_LOCKASSERT(MA_OWNED); 444101774Salfred SLIST_FOREACH(suptr, &semu_list, un_next) { 445101350Salfred struct undo *sunptr = &suptr->un_ent[0]; 446101350Salfred int i = 0; 4472729Sdfr 4482729Sdfr while (i < suptr->un_cnt) { 4492729Sdfr if (sunptr->un_id == semid) { 4502729Sdfr if (semnum == -1 || sunptr->un_num == semnum) { 4512729Sdfr suptr->un_cnt--; 4522729Sdfr if (i < suptr->un_cnt) { 4532729Sdfr suptr->un_ent[i] = 4542729Sdfr suptr->un_ent[suptr->un_cnt]; 4552729Sdfr continue; 4562729Sdfr } 4572729Sdfr } 4582729Sdfr if (semnum != -1) 4592729Sdfr break; 4602729Sdfr } 4612729Sdfr i++, sunptr++; 4622729Sdfr } 4632729Sdfr } 4642729Sdfr} 4652729Sdfr 466101774Salfredstatic int 467101774Salfredsemvalid(semid, semaptr) 468101774Salfred int semid; 469101774Salfred struct semid_ds *semaptr; 470101774Salfred{ 471101774Salfred 472101774Salfred return ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 473101774Salfred semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0); 474101774Salfred} 475101774Salfred 47612866Speter/* 47712866Speter * Note that the user-mode half of this passes a union, not a pointer 47812866Speter */ 47912866Speter#ifndef _SYS_SYSPROTO_H_ 48012866Speterstruct __semctl_args { 4812729Sdfr int semid; 4822729Sdfr int semnum; 4832729Sdfr int cmd; 4842729Sdfr union semun *arg; 4852729Sdfr}; 48612866Speter#endif 4872729Sdfr 48882607Sdillon/* 48982607Sdillon * MPSAFE 49082607Sdillon */ 49112866Speterint 49283366Sjulian__semctl(td, uap) 49383366Sjulian struct thread *td; 494101350Salfred struct __semctl_args *uap; 4952729Sdfr{ 4962729Sdfr int semid = uap->semid; 4972729Sdfr int semnum = uap->semnum; 4982729Sdfr int cmd = uap->cmd; 499101774Salfred u_short *array; 5002729Sdfr union semun *arg = uap->arg; 5012729Sdfr union semun real_arg; 50291406Sjhb struct ucred *cred = td->td_ucred; 50382607Sdillon int i, rval, error; 5042729Sdfr struct semid_ds sbuf; 505101350Salfred struct semid_ds *semaptr; 506101774Salfred struct mtx *sema_mtxp; 507101774Salfred u_short usval, count; 5082729Sdfr 509100523Salfred DPRINTF(("call to semctl(%d, %d, %d, 0x%x)\n", 510100523Salfred semid, semnum, cmd, arg)); 51191703Sjhb if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 51291703Sjhb return (ENOSYS); 51391703Sjhb 514101774Salfred array = NULL; 515101774Salfred 51683414Smr switch(cmd) { 51783414Smr case SEM_STAT: 51891744Smaxim if (semid < 0 || semid >= seminfo.semmni) 519101774Salfred return (EINVAL); 520101774Salfred if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 521101774Salfred return (error); 52283414Smr semaptr = &sema[semid]; 523101774Salfred sema_mtxp = &sema_mtx[semid]; 524101774Salfred mtx_lock(sema_mtxp); 525101774Salfred if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ) { 526101774Salfred error = EINVAL; 527101774Salfred goto done2; 528101774Salfred } 52983414Smr if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 530101774Salfred goto done2; 531101774Salfred mtx_unlock(sema_mtxp); 532100511Salfred error = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds)); 53383414Smr rval = IXSEQ_TO_IPCID(semid,semaptr->sem_perm); 53483414Smr if (error == 0) 53583414Smr td->td_retval[0] = rval; 536101774Salfred return (error); 53783414Smr } 53883414Smr 5392729Sdfr semid = IPCID_TO_IX(semid); 540101774Salfred if (semid < 0 || semid >= seminfo.semmni) 541101774Salfred return (EINVAL); 5422729Sdfr 5432729Sdfr semaptr = &sema[semid]; 544101774Salfred sema_mtxp = &sema_mtx[semid]; 545101774Salfred 54682607Sdillon error = 0; 5472729Sdfr rval = 0; 5482729Sdfr 5492729Sdfr switch (cmd) { 5502729Sdfr case IPC_RMID: 551101774Salfred mtx_lock(sema_mtxp); 552101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 553101774Salfred goto done2; 55483366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M))) 55582607Sdillon goto done2; 5562729Sdfr semaptr->sem_perm.cuid = cred->cr_uid; 5572729Sdfr semaptr->sem_perm.uid = cred->cr_uid; 5582729Sdfr semtot -= semaptr->sem_nsems; 5592729Sdfr for (i = semaptr->sem_base - sem; i < semtot; i++) 5602729Sdfr sem[i] = sem[i + semaptr->sem_nsems]; 5612729Sdfr for (i = 0; i < seminfo.semmni; i++) { 5622729Sdfr if ((sema[i].sem_perm.mode & SEM_ALLOC) && 5632729Sdfr sema[i].sem_base > semaptr->sem_base) 5642729Sdfr sema[i].sem_base -= semaptr->sem_nsems; 5652729Sdfr } 5662729Sdfr semaptr->sem_perm.mode = 0; 567101774Salfred SEMUNDO_LOCK(); 5682729Sdfr semundo_clear(semid, -1); 569101774Salfred SEMUNDO_UNLOCK(); 570100511Salfred wakeup(semaptr); 5712729Sdfr break; 5722729Sdfr 5732729Sdfr case IPC_SET: 57482607Sdillon if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 57582607Sdillon goto done2; 576101774Salfred if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0) 57782607Sdillon goto done2; 578101774Salfred mtx_lock(sema_mtxp); 579101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 580101774Salfred goto done2; 581101774Salfred if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M))) 582101774Salfred goto done2; 5832729Sdfr semaptr->sem_perm.uid = sbuf.sem_perm.uid; 5842729Sdfr semaptr->sem_perm.gid = sbuf.sem_perm.gid; 5852729Sdfr semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 5862729Sdfr (sbuf.sem_perm.mode & 0777); 58734961Sphk semaptr->sem_ctime = time_second; 5882729Sdfr break; 5892729Sdfr 5902729Sdfr case IPC_STAT: 591101774Salfred if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 592101774Salfred goto done2; 593101774Salfred mtx_lock(sema_mtxp); 594101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 595101774Salfred goto done2; 59683366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 59782607Sdillon goto done2; 598101774Salfred sbuf = *semaptr; 599101774Salfred mtx_unlock(sema_mtxp); 600100511Salfred error = copyout(semaptr, real_arg.buf, 60182607Sdillon sizeof(struct semid_ds)); 6022729Sdfr break; 6032729Sdfr 6042729Sdfr case GETNCNT: 605101774Salfred mtx_lock(sema_mtxp); 606101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 607101774Salfred goto done2; 60883366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 60982607Sdillon goto done2; 61082607Sdillon if (semnum < 0 || semnum >= semaptr->sem_nsems) { 61182607Sdillon error = EINVAL; 61282607Sdillon goto done2; 61382607Sdillon } 6142729Sdfr rval = semaptr->sem_base[semnum].semncnt; 6152729Sdfr break; 6162729Sdfr 6172729Sdfr case GETPID: 618101774Salfred mtx_lock(sema_mtxp); 619101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 620101774Salfred goto done2; 62183366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 62282607Sdillon goto done2; 62382607Sdillon if (semnum < 0 || semnum >= semaptr->sem_nsems) { 62482607Sdillon error = EINVAL; 62582607Sdillon goto done2; 62682607Sdillon } 6272729Sdfr rval = semaptr->sem_base[semnum].sempid; 6282729Sdfr break; 6292729Sdfr 6302729Sdfr case GETVAL: 631101774Salfred mtx_lock(sema_mtxp); 632101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 633101774Salfred goto done2; 63483366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 63582607Sdillon goto done2; 63682607Sdillon if (semnum < 0 || semnum >= semaptr->sem_nsems) { 63782607Sdillon error = EINVAL; 63882607Sdillon goto done2; 63982607Sdillon } 6402729Sdfr rval = semaptr->sem_base[semnum].semval; 6412729Sdfr break; 6422729Sdfr 6432729Sdfr case GETALL: 644101774Salfred if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 645101774Salfred goto done2; 646101774Salfred array = malloc(sizeof(*array) * semaptr->sem_nsems, M_TEMP, 647111119Simp M_WAITOK); 648101774Salfred mtx_lock(sema_mtxp); 649101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 650101774Salfred goto done2; 65183366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 65282607Sdillon goto done2; 653101774Salfred for (i = 0; i < semaptr->sem_nsems; i++) 654101774Salfred array[i] = semaptr->sem_base[i].semval; 655101774Salfred mtx_unlock(sema_mtxp); 656101774Salfred error = copyout(array, real_arg.array, 657101774Salfred i * sizeof(real_arg.array[0])); 6582729Sdfr break; 6592729Sdfr 6602729Sdfr case GETZCNT: 661101774Salfred mtx_lock(sema_mtxp); 662101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 663101774Salfred goto done2; 66483366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R))) 66582607Sdillon goto done2; 66682607Sdillon if (semnum < 0 || semnum >= semaptr->sem_nsems) { 66782607Sdillon error = EINVAL; 66882607Sdillon goto done2; 66982607Sdillon } 6702729Sdfr rval = semaptr->sem_base[semnum].semzcnt; 6712729Sdfr break; 6722729Sdfr 6732729Sdfr case SETVAL: 674101774Salfred if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 675101774Salfred goto done2; 676101774Salfred mtx_lock(sema_mtxp); 677101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 678101774Salfred goto done2; 67983366Sjulian if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W))) 68082607Sdillon goto done2; 68182607Sdillon if (semnum < 0 || semnum >= semaptr->sem_nsems) { 68282607Sdillon error = EINVAL; 68382607Sdillon goto done2; 68482607Sdillon } 68584789Smr if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) { 68684789Smr error = ERANGE; 68784789Smr goto done2; 68884789Smr } 6892729Sdfr semaptr->sem_base[semnum].semval = real_arg.val; 690101774Salfred SEMUNDO_LOCK(); 6912729Sdfr semundo_clear(semid, semnum); 692101774Salfred SEMUNDO_UNLOCK(); 693100511Salfred wakeup(semaptr); 6942729Sdfr break; 6952729Sdfr 6962729Sdfr case SETALL: 697101774Salfred mtx_lock(sema_mtxp); 698101774Salfredraced: 699101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 70082607Sdillon goto done2; 701101774Salfred count = semaptr->sem_nsems; 702101774Salfred mtx_unlock(sema_mtxp); 70382607Sdillon if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 70482607Sdillon goto done2; 705111119Simp array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); 706101774Salfred copyin(real_arg.array, array, count * sizeof(*array)); 707101774Salfred if (error) 708101774Salfred break; 709101774Salfred mtx_lock(sema_mtxp); 710101774Salfred if ((error = semvalid(uap->semid, semaptr)) != 0) 711101774Salfred goto done2; 712101774Salfred /* we could have raced? */ 713101774Salfred if (count != semaptr->sem_nsems) { 714101774Salfred free(array, M_TEMP); 715101774Salfred array = NULL; 716101774Salfred goto raced; 717101774Salfred } 718101774Salfred if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W))) 719101774Salfred goto done2; 7202729Sdfr for (i = 0; i < semaptr->sem_nsems; i++) { 721101774Salfred usval = array[i]; 72284789Smr if (usval > seminfo.semvmx) { 72384789Smr error = ERANGE; 72484789Smr break; 72584789Smr } 72684789Smr semaptr->sem_base[i].semval = usval; 7272729Sdfr } 728101774Salfred SEMUNDO_LOCK(); 7292729Sdfr semundo_clear(semid, -1); 730101774Salfred SEMUNDO_UNLOCK(); 731100511Salfred wakeup(semaptr); 7322729Sdfr break; 7332729Sdfr 7342729Sdfr default: 73582607Sdillon error = EINVAL; 73682607Sdillon break; 7372729Sdfr } 7382729Sdfr 73982607Sdillon if (error == 0) 74083366Sjulian td->td_retval[0] = rval; 74182607Sdillondone2: 742101774Salfred if (mtx_owned(sema_mtxp)) 743101774Salfred mtx_unlock(sema_mtxp); 744101774Salfred if (array != NULL) 745101774Salfred free(array, M_TEMP); 74682607Sdillon return(error); 7472729Sdfr} 7482729Sdfr 74912866Speter#ifndef _SYS_SYSPROTO_H_ 7502729Sdfrstruct semget_args { 7512729Sdfr key_t key; 7522729Sdfr int nsems; 7532729Sdfr int semflg; 7542729Sdfr}; 75512866Speter#endif 7562729Sdfr 75782607Sdillon/* 75882607Sdillon * MPSAFE 75982607Sdillon */ 76012866Speterint 76183366Sjuliansemget(td, uap) 76283366Sjulian struct thread *td; 763101350Salfred struct semget_args *uap; 7642729Sdfr{ 76582607Sdillon int semid, error = 0; 7662729Sdfr int key = uap->key; 7672729Sdfr int nsems = uap->nsems; 7682729Sdfr int semflg = uap->semflg; 76991703Sjhb struct ucred *cred = td->td_ucred; 7702729Sdfr 771100523Salfred DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg)); 77291703Sjhb if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 77391703Sjhb return (ENOSYS); 77491703Sjhb 77582607Sdillon mtx_lock(&Giant); 7762729Sdfr if (key != IPC_PRIVATE) { 7772729Sdfr for (semid = 0; semid < seminfo.semmni; semid++) { 7782729Sdfr if ((sema[semid].sem_perm.mode & SEM_ALLOC) && 7792729Sdfr sema[semid].sem_perm.key == key) 7802729Sdfr break; 7812729Sdfr } 7822729Sdfr if (semid < seminfo.semmni) { 783100523Salfred DPRINTF(("found public key\n")); 78483366Sjulian if ((error = ipcperm(td, &sema[semid].sem_perm, 78582607Sdillon semflg & 0700))) { 78682607Sdillon goto done2; 78782607Sdillon } 7882729Sdfr if (nsems > 0 && sema[semid].sem_nsems < nsems) { 789100523Salfred DPRINTF(("too small\n")); 79082607Sdillon error = EINVAL; 79182607Sdillon goto done2; 7922729Sdfr } 7932729Sdfr if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 794100523Salfred DPRINTF(("not exclusive\n")); 79582607Sdillon error = EEXIST; 79682607Sdillon goto done2; 7972729Sdfr } 7982729Sdfr goto found; 7992729Sdfr } 8002729Sdfr } 8012729Sdfr 802100523Salfred DPRINTF(("need to allocate the semid_ds\n")); 8032729Sdfr if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 8042729Sdfr if (nsems <= 0 || nsems > seminfo.semmsl) { 805100523Salfred DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, 806100523Salfred seminfo.semmsl)); 80782607Sdillon error = EINVAL; 80882607Sdillon goto done2; 8092729Sdfr } 8102729Sdfr if (nsems > seminfo.semmns - semtot) { 811100523Salfred DPRINTF(( 812100523Salfred "not enough semaphores left (need %d, got %d)\n", 813100523Salfred nsems, seminfo.semmns - semtot)); 81482607Sdillon error = ENOSPC; 81582607Sdillon goto done2; 8162729Sdfr } 8172729Sdfr for (semid = 0; semid < seminfo.semmni; semid++) { 8182729Sdfr if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) 8192729Sdfr break; 8202729Sdfr } 8212729Sdfr if (semid == seminfo.semmni) { 822100523Salfred DPRINTF(("no more semid_ds's available\n")); 82382607Sdillon error = ENOSPC; 82482607Sdillon goto done2; 8252729Sdfr } 826100523Salfred DPRINTF(("semid %d is available\n", semid)); 8272729Sdfr sema[semid].sem_perm.key = key; 8282729Sdfr sema[semid].sem_perm.cuid = cred->cr_uid; 8292729Sdfr sema[semid].sem_perm.uid = cred->cr_uid; 8302729Sdfr sema[semid].sem_perm.cgid = cred->cr_gid; 8312729Sdfr sema[semid].sem_perm.gid = cred->cr_gid; 8322729Sdfr sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; 8332729Sdfr sema[semid].sem_perm.seq = 8342729Sdfr (sema[semid].sem_perm.seq + 1) & 0x7fff; 8352729Sdfr sema[semid].sem_nsems = nsems; 8362729Sdfr sema[semid].sem_otime = 0; 83734961Sphk sema[semid].sem_ctime = time_second; 8382729Sdfr sema[semid].sem_base = &sem[semtot]; 8392729Sdfr semtot += nsems; 8402729Sdfr bzero(sema[semid].sem_base, 8412729Sdfr sizeof(sema[semid].sem_base[0])*nsems); 842100523Salfred DPRINTF(("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, 843100523Salfred &sem[semtot])); 8442729Sdfr } else { 845100523Salfred DPRINTF(("didn't find it and wasn't asked to create it\n")); 84682607Sdillon error = ENOENT; 84782607Sdillon goto done2; 8482729Sdfr } 8492729Sdfr 8502729Sdfrfound: 85183366Sjulian td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); 85282607Sdillondone2: 85382607Sdillon mtx_unlock(&Giant); 85482607Sdillon return (error); 8552729Sdfr} 8562729Sdfr 85712866Speter#ifndef _SYS_SYSPROTO_H_ 8582729Sdfrstruct semop_args { 8592729Sdfr int semid; 8602729Sdfr struct sembuf *sops; 861109829Salfred size_t nsops; 8622729Sdfr}; 86312866Speter#endif 8642729Sdfr 86582607Sdillon/* 86682607Sdillon * MPSAFE 86782607Sdillon */ 86812866Speterint 86983366Sjuliansemop(td, uap) 87083366Sjulian struct thread *td; 871101350Salfred struct semop_args *uap; 8722729Sdfr{ 8732729Sdfr int semid = uap->semid; 874109829Salfred size_t nsops = uap->nsops; 875105429Salfred struct sembuf *sops; 876101350Salfred struct semid_ds *semaptr; 877101350Salfred struct sembuf *sopptr = 0; 878101350Salfred struct sem *semptr = 0; 87984789Smr struct sem_undo *suptr; 880101774Salfred struct mtx *sema_mtxp; 881110040Stjr size_t i, j, k; 882109829Salfred int error; 8833308Sphk int do_wakeup, do_undos; 8842729Sdfr 885100523Salfred DPRINTF(("call to semop(%d, 0x%x, %u)\n", semid, sops, nsops)); 8862729Sdfr 88791703Sjhb if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 88891703Sjhb return (ENOSYS); 88991703Sjhb 8902729Sdfr semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 8912729Sdfr 892101774Salfred if (semid < 0 || semid >= seminfo.semmni) 893101774Salfred return (EINVAL); 894101774Salfred 895101774Salfred /* Allocate memory for sem_ops */ 896101774Salfred if (nsops > seminfo.semopm) { 897101774Salfred DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, 898101774Salfred nsops)); 899101774Salfred return (E2BIG); 90082607Sdillon } 901111119Simp sops = malloc(nsops * sizeof(sops[0]), M_SEM, M_WAITOK); 902101774Salfred if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) { 903101774Salfred DPRINTF(("error = %d from copyin(%08x, %08x, %d)\n", error, 904101774Salfred uap->sops, sops, nsops * sizeof(sops[0]))); 905101774Salfred free(sops, M_SEM); 906101774Salfred return (error); 907101774Salfred } 9082729Sdfr 9092729Sdfr semaptr = &sema[semid]; 910101774Salfred sema_mtxp = &sema_mtx[semid]; 911101774Salfred mtx_lock(sema_mtxp); 91282607Sdillon if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) { 91382607Sdillon error = EINVAL; 91482607Sdillon goto done2; 91582607Sdillon } 91682607Sdillon if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 91782607Sdillon error = EINVAL; 91882607Sdillon goto done2; 91982607Sdillon } 92084789Smr /* 92184789Smr * Initial pass thru sops to see what permissions are needed. 92284789Smr * Also perform any checks that don't need repeating on each 92384789Smr * attempt to satisfy the request vector. 92484789Smr */ 92584789Smr j = 0; /* permission needed */ 92684789Smr do_undos = 0; 92784789Smr for (i = 0; i < nsops; i++) { 92884789Smr sopptr = &sops[i]; 92984789Smr if (sopptr->sem_num >= semaptr->sem_nsems) { 93084789Smr error = EFBIG; 93184789Smr goto done2; 93284789Smr } 93384789Smr if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0) 93484789Smr do_undos = 1; 93584789Smr j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A; 93684789Smr } 93784789Smr 93884789Smr if ((error = ipcperm(td, &semaptr->sem_perm, j))) { 939100523Salfred DPRINTF(("error = %d from ipaccess\n", error)); 94082607Sdillon goto done2; 9412729Sdfr } 9422729Sdfr 9438876Srgrimes /* 9442729Sdfr * Loop trying to satisfy the vector of requests. 9452729Sdfr * If we reach a point where we must wait, any requests already 9462729Sdfr * performed are rolled back and we go to sleep until some other 9472729Sdfr * process wakes us up. At this point, we start all over again. 9482729Sdfr * 9492729Sdfr * This ensures that from the perspective of other tasks, a set 9502729Sdfr * of requests is atomic (never partially satisfied). 9512729Sdfr */ 9522729Sdfr for (;;) { 9532729Sdfr do_wakeup = 0; 95484789Smr error = 0; /* error return if necessary */ 9552729Sdfr 9562729Sdfr for (i = 0; i < nsops; i++) { 9572729Sdfr sopptr = &sops[i]; 9582729Sdfr semptr = &semaptr->sem_base[sopptr->sem_num]; 9592729Sdfr 960100523Salfred DPRINTF(( 961100523Salfred "semop: semaptr=%x, sem_base=%x, " 962100523Salfred "semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 9632729Sdfr semaptr, semaptr->sem_base, semptr, 9642729Sdfr sopptr->sem_num, semptr->semval, sopptr->sem_op, 965100523Salfred (sopptr->sem_flg & IPC_NOWAIT) ? 966100523Salfred "nowait" : "wait")); 9672729Sdfr 9682729Sdfr if (sopptr->sem_op < 0) { 9692729Sdfr if (semptr->semval + sopptr->sem_op < 0) { 970100523Salfred DPRINTF(("semop: can't do it now\n")); 9712729Sdfr break; 9722729Sdfr } else { 9732729Sdfr semptr->semval += sopptr->sem_op; 9742729Sdfr if (semptr->semval == 0 && 9752729Sdfr semptr->semzcnt > 0) 9762729Sdfr do_wakeup = 1; 9772729Sdfr } 9782729Sdfr } else if (sopptr->sem_op == 0) { 97984789Smr if (semptr->semval != 0) { 980100523Salfred DPRINTF(("semop: not zero now\n")); 9812729Sdfr break; 9822729Sdfr } 98384789Smr } else if (semptr->semval + sopptr->sem_op > 98484789Smr seminfo.semvmx) { 98584789Smr error = ERANGE; 98684789Smr break; 9872729Sdfr } else { 9882729Sdfr if (semptr->semncnt > 0) 9892729Sdfr do_wakeup = 1; 9902729Sdfr semptr->semval += sopptr->sem_op; 9912729Sdfr } 9922729Sdfr } 9932729Sdfr 9942729Sdfr /* 9952729Sdfr * Did we get through the entire vector? 9962729Sdfr */ 9972729Sdfr if (i >= nsops) 9982729Sdfr goto done; 9992729Sdfr 10002729Sdfr /* 10012729Sdfr * No ... rollback anything that we've already done 10022729Sdfr */ 1003100523Salfred DPRINTF(("semop: rollback 0 through %d\n", i-1)); 10042729Sdfr for (j = 0; j < i; j++) 10052729Sdfr semaptr->sem_base[sops[j].sem_num].semval -= 10062729Sdfr sops[j].sem_op; 10072729Sdfr 100884789Smr /* If we detected an error, return it */ 100984789Smr if (error != 0) 101084789Smr goto done2; 101184789Smr 10122729Sdfr /* 10132729Sdfr * If the request that we couldn't satisfy has the 10142729Sdfr * NOWAIT flag set then return with EAGAIN. 10152729Sdfr */ 101682607Sdillon if (sopptr->sem_flg & IPC_NOWAIT) { 101782607Sdillon error = EAGAIN; 101882607Sdillon goto done2; 101982607Sdillon } 10202729Sdfr 10212729Sdfr if (sopptr->sem_op == 0) 10222729Sdfr semptr->semzcnt++; 10232729Sdfr else 10242729Sdfr semptr->semncnt++; 10252729Sdfr 1026100523Salfred DPRINTF(("semop: good night!\n")); 1027101774Salfred error = msleep(semaptr, sema_mtxp, (PZERO - 4) | PCATCH, 1028101774Salfred "semwait", 0); 1029100523Salfred DPRINTF(("semop: good morning (error=%d)!\n", error)); 10302729Sdfr 103182607Sdillon if (error != 0) { 103282607Sdillon error = EINTR; 103382607Sdillon goto done2; 103482607Sdillon } 1035100523Salfred DPRINTF(("semop: good morning!\n")); 10362729Sdfr 10372729Sdfr /* 10382729Sdfr * Make sure that the semaphore still exists 10392729Sdfr */ 10402729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 104182607Sdillon semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { 104282607Sdillon error = EIDRM; 104382607Sdillon goto done2; 104482607Sdillon } 10452729Sdfr 10462729Sdfr /* 10472729Sdfr * The semaphore is still alive. Readjust the count of 10482729Sdfr * waiting processes. 10492729Sdfr */ 10502729Sdfr if (sopptr->sem_op == 0) 10512729Sdfr semptr->semzcnt--; 10522729Sdfr else 10532729Sdfr semptr->semncnt--; 10542729Sdfr } 10552729Sdfr 10562729Sdfrdone: 10572729Sdfr /* 10582729Sdfr * Process any SEM_UNDO requests. 10592729Sdfr */ 10602729Sdfr if (do_undos) { 1061101774Salfred SEMUNDO_LOCK(); 106284789Smr suptr = NULL; 10632729Sdfr for (i = 0; i < nsops; i++) { 10642729Sdfr /* 10652729Sdfr * We only need to deal with SEM_UNDO's for non-zero 10662729Sdfr * op's. 10672729Sdfr */ 10682729Sdfr int adjval; 10692729Sdfr 10702729Sdfr if ((sops[i].sem_flg & SEM_UNDO) == 0) 10712729Sdfr continue; 10722729Sdfr adjval = sops[i].sem_op; 10732729Sdfr if (adjval == 0) 10742729Sdfr continue; 107583366Sjulian error = semundo_adjust(td, &suptr, semid, 10762729Sdfr sops[i].sem_num, -adjval); 107782607Sdillon if (error == 0) 10782729Sdfr continue; 10792729Sdfr 10802729Sdfr /* 10812729Sdfr * Oh-Oh! We ran out of either sem_undo's or undo's. 10822729Sdfr * Rollback the adjustments to this point and then 10832729Sdfr * rollback the semaphore ups and down so we can return 10842729Sdfr * with an error with all structures restored. We 10852729Sdfr * rollback the undo's in the exact reverse order that 10862729Sdfr * we applied them. This guarantees that we won't run 10872729Sdfr * out of space as we roll things back out. 10882729Sdfr */ 1089110040Stjr for (j = 0; j < i; j++) { 1090110040Stjr k = i - j - 1; 1091110040Stjr if ((sops[k].sem_flg & SEM_UNDO) == 0) 10922729Sdfr continue; 1093110040Stjr adjval = sops[k].sem_op; 10942729Sdfr if (adjval == 0) 10952729Sdfr continue; 109683366Sjulian if (semundo_adjust(td, &suptr, semid, 1097110040Stjr sops[k].sem_num, adjval) != 0) 10982729Sdfr panic("semop - can't undo undos"); 10992729Sdfr } 11002729Sdfr 11012729Sdfr for (j = 0; j < nsops; j++) 11022729Sdfr semaptr->sem_base[sops[j].sem_num].semval -= 11032729Sdfr sops[j].sem_op; 11042729Sdfr 1105100523Salfred DPRINTF(("error = %d from semundo_adjust\n", error)); 1106101774Salfred SEMUNDO_UNLOCK(); 110782607Sdillon goto done2; 11082729Sdfr } /* loop through the sops */ 1109101774Salfred SEMUNDO_UNLOCK(); 11102729Sdfr } /* if (do_undos) */ 11112729Sdfr 111284789Smr /* We're definitely done - set the sempid's and time */ 11132729Sdfr for (i = 0; i < nsops; i++) { 11142729Sdfr sopptr = &sops[i]; 11152729Sdfr semptr = &semaptr->sem_base[sopptr->sem_num]; 111683366Sjulian semptr->sempid = td->td_proc->p_pid; 11172729Sdfr } 111884789Smr semaptr->sem_otime = time_second; 11192729Sdfr 112084789Smr /* 112184789Smr * Do a wakeup if any semaphore was up'd whilst something was 112284789Smr * sleeping on it. 112384789Smr */ 11242729Sdfr if (do_wakeup) { 1125100523Salfred DPRINTF(("semop: doing wakeup\n")); 1126100511Salfred wakeup(semaptr); 1127100523Salfred DPRINTF(("semop: back from wakeup\n")); 11282729Sdfr } 1129100523Salfred DPRINTF(("semop: done\n")); 113083366Sjulian td->td_retval[0] = 0; 113182607Sdillondone2: 1132101774Salfred mtx_unlock(sema_mtxp); 1133105429Salfred free(sops, M_SEM); 113482607Sdillon return (error); 11352729Sdfr} 11362729Sdfr 11372729Sdfr/* 11382729Sdfr * Go through the undo structures for this process and apply the adjustments to 11392729Sdfr * semaphores. 11402729Sdfr */ 114169449Salfredstatic void 114269449Salfredsemexit_myhook(p) 11432729Sdfr struct proc *p; 11442729Sdfr{ 1145101350Salfred struct sem_undo *suptr; 1146101350Salfred struct sem_undo **supptr; 11472729Sdfr 11482729Sdfr /* 11492729Sdfr * Go through the chain of undo vectors looking for one 11502729Sdfr * associated with this process. 11512729Sdfr */ 1152101774Salfred SEMUNDO_LOCK(); 1153101774Salfred SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) { 11542729Sdfr if (suptr->un_proc == p) 11552729Sdfr break; 11562729Sdfr } 1157101774Salfred SEMUNDO_UNLOCK(); 11582729Sdfr 11592729Sdfr if (suptr == NULL) 116059828Speter return; 11612729Sdfr 1162100523Salfred DPRINTF(("proc @%08x has undo structure with %d entries\n", p, 1163100523Salfred suptr->un_cnt)); 11642729Sdfr 11652729Sdfr /* 11662729Sdfr * If there are any active undo elements then process them. 11672729Sdfr */ 11682729Sdfr if (suptr->un_cnt > 0) { 11692729Sdfr int ix; 11702729Sdfr 11712729Sdfr for (ix = 0; ix < suptr->un_cnt; ix++) { 11722729Sdfr int semid = suptr->un_ent[ix].un_id; 11732729Sdfr int semnum = suptr->un_ent[ix].un_num; 11742729Sdfr int adjval = suptr->un_ent[ix].un_adjval; 11752729Sdfr struct semid_ds *semaptr; 1176101774Salfred struct mtx *sema_mtxp; 11772729Sdfr 11782729Sdfr semaptr = &sema[semid]; 1179101774Salfred sema_mtxp = &sema_mtx[semid]; 1180101774Salfred mtx_lock(sema_mtxp); 1181101774Salfred SEMUNDO_LOCK(); 11822729Sdfr if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 11832729Sdfr panic("semexit - semid not allocated"); 11842729Sdfr if (semnum >= semaptr->sem_nsems) 11852729Sdfr panic("semexit - semnum out of range"); 11862729Sdfr 1187100523Salfred DPRINTF(( 1188100523Salfred "semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", 11892729Sdfr suptr->un_proc, suptr->un_ent[ix].un_id, 11902729Sdfr suptr->un_ent[ix].un_num, 11912729Sdfr suptr->un_ent[ix].un_adjval, 1192100523Salfred semaptr->sem_base[semnum].semval)); 11932729Sdfr 11942729Sdfr if (adjval < 0) { 11952729Sdfr if (semaptr->sem_base[semnum].semval < -adjval) 11962729Sdfr semaptr->sem_base[semnum].semval = 0; 11972729Sdfr else 11982729Sdfr semaptr->sem_base[semnum].semval += 11992729Sdfr adjval; 12002729Sdfr } else 12012729Sdfr semaptr->sem_base[semnum].semval += adjval; 12022729Sdfr 1203100511Salfred wakeup(semaptr); 1204100523Salfred DPRINTF(("semexit: back from wakeup\n")); 1205101774Salfred mtx_unlock(sema_mtxp); 1206101774Salfred SEMUNDO_UNLOCK(); 12072729Sdfr } 12082729Sdfr } 12092729Sdfr 12102729Sdfr /* 12112729Sdfr * Deallocate the undo vector. 12122729Sdfr */ 1213100523Salfred DPRINTF(("removing vector\n")); 12142729Sdfr suptr->un_proc = NULL; 1215101774Salfred *supptr = SLIST_NEXT(suptr, un_next); 12162729Sdfr} 121777461Sdd 121877461Sddstatic int 121977461Sddsysctl_sema(SYSCTL_HANDLER_ARGS) 122077461Sdd{ 122177461Sdd 122277461Sdd return (SYSCTL_OUT(req, sema, 122377461Sdd sizeof(struct semid_ds) * seminfo.semmni)); 122477461Sdd} 1225