#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/cred.h>
#include <sys/vmem.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/ipc_impl.h>
#include <sys/sem.h>
#include <sys/sem_impl.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/cpuvar.h>
#include <sys/debug.h>
#include <sys/var.h>
#include <sys/cmn_err.h>
#include <sys/modctl.h>
#include <sys/syscall.h>
#include <sys/avl.h>
#include <sys/list.h>
#include <sys/zone.h>
#include <c2/audit.h>
extern rctl_hndl_t rc_zone_semmni;
extern rctl_hndl_t rc_project_semmni;
extern rctl_hndl_t rc_process_semmsl;
extern rctl_hndl_t rc_process_semopm;
static ipc_service_t *sem_svc;
static zone_key_t sem_zone_key;
int seminfo_semaem = 16384;
int seminfo_semmap = 10;
int seminfo_semmni = 10;
int seminfo_semmns = 60;
int seminfo_semmnu = 30;
int seminfo_semmsl = 25;
int seminfo_semopm = 10;
int seminfo_semume = 10;
int seminfo_semusz = 96;
int seminfo_semvmx = 32767;
#define SEM_MAXUCOPS 4096
#define SEM_UNDOSZ(n) (sizeof (struct sem_undo) + (n - 1) * sizeof (int))
static int semsys(int opcode, uintptr_t a0, uintptr_t a1,
uintptr_t a2, uintptr_t a3);
static void sem_dtor(kipc_perm_t *);
static void sem_rmid(kipc_perm_t *);
static void sem_remove_zone(zoneid_t, void *);
static struct sysent ipcsem_sysent = {
5,
SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
semsys
};
static struct modlsys modlsys = {
&mod_syscallops, "System V semaphore facility", &ipcsem_sysent
};
#ifdef _SYSCALL32_IMPL
static struct modlsys modlsys32 = {
&mod_syscallops32, "32-bit System V semaphore facility", &ipcsem_sysent
};
#endif
static struct modlinkage modlinkage = {
MODREV_1,
&modlsys,
#ifdef _SYSCALL32_IMPL
&modlsys32,
#endif
NULL
};
int
_init(void)
{
int result;
sem_svc = ipcs_create("semids", rc_project_semmni, rc_zone_semmni,
sizeof (ksemid_t), sem_dtor, sem_rmid, AT_IPC_SEM,
offsetof(ipc_rqty_t, ipcq_semmni));
zone_key_create(&sem_zone_key, NULL, sem_remove_zone, NULL);
if ((result = mod_install(&modlinkage)) == 0)
return (0);
(void) zone_key_delete(sem_zone_key);
ipcs_destroy(sem_svc);
return (result);
}
int
_fini(void)
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static void
sem_dtor(kipc_perm_t *perm)
{
ksemid_t *sp = (ksemid_t *)perm;
kmem_free(sp->sem_base,
P2ROUNDUP(sp->sem_nsems * sizeof (struct sem), 64));
list_destroy(&sp->sem_undos);
}
static int
sem_undo_add(short val, ushort_t num, struct sem_undo *undo)
{
int newval = undo->un_aoe[num] - val;
if (newval > USHRT_MAX || newval < -USHRT_MAX)
return (ERANGE);
undo->un_aoe[num] = newval;
return (0);
}
static void
sem_undo_clear(ksemid_t *sp, ushort_t low, ushort_t high)
{
struct sem_undo *undo;
int i;
ASSERT(low <= high);
ASSERT(high < sp->sem_nsems);
for (undo = list_head(&sp->sem_undos); undo;
undo = list_next(&sp->sem_undos, undo))
for (i = low; i <= high; i++)
undo->un_aoe[i] = 0;
}
static void
sem_rollback(ksemid_t *sp, struct sembuf *op, int n, struct sem_undo *undo)
{
struct sem *semp;
for (op += n - 1; n--; op--) {
if (op->sem_op == 0)
continue;
semp = &sp->sem_base[op->sem_num];
semp->semval -= op->sem_op;
if (op->sem_flg & SEM_UNDO) {
ASSERT(undo != NULL);
(void) sem_undo_add(-op->sem_op, op->sem_num, undo);
}
}
}
static void
sem_rmid(kipc_perm_t *perm)
{
ksemid_t *sp = (ksemid_t *)perm;
struct sem *semp;
struct sem_undo *undo;
size_t size = SEM_UNDOSZ(sp->sem_nsems);
int i;
while (undo = list_head(&sp->sem_undos)) {
list_remove(&sp->sem_undos, undo);
mutex_enter(&undo->un_proc->p_lock);
if (undo->un_proc->p_semacct == NULL) {
mutex_exit(&undo->un_proc->p_lock);
continue;
}
avl_remove(undo->un_proc->p_semacct, undo);
mutex_exit(&undo->un_proc->p_lock);
kmem_free(undo, size);
ipc_rele_locked(sem_svc, (kipc_perm_t *)sp);
}
for (i = 0; i < sp->sem_nsems; i++) {
semp = &sp->sem_base[i];
semp->semval = semp->sempid = 0;
if (semp->semncnt) {
cv_broadcast(&semp->semncnt_cv);
semp->semncnt = 0;
}
if (semp->semzcnt) {
cv_broadcast(&semp->semzcnt_cv);
semp->semzcnt = 0;
}
}
}
static int
semctl(int semid, uint_t semnum, int cmd, uintptr_t arg)
{
ksemid_t *sp;
struct sem *p;
unsigned int i;
ushort_t *vals, *vp;
size_t vsize = 0;
int error = 0;
int retval = 0;
struct cred *cr;
kmutex_t *lock;
model_t mdl = get_udatamodel();
STRUCT_DECL(semid_ds, sid);
struct semid_ds64 ds64;
STRUCT_INIT(sid, mdl);
cr = CRED();
switch (cmd) {
case IPC_SET:
if (copyin((void *)arg, STRUCT_BUF(sid), STRUCT_SIZE(sid)))
return (set_errno(EFAULT));
break;
case IPC_SET64:
if (copyin((void *)arg, &ds64, sizeof (struct semid_ds64)))
return (set_errno(EFAULT));
break;
case SETALL:
if ((lock = ipc_lookup(sem_svc, semid,
(kipc_perm_t **)&sp)) == NULL)
return (set_errno(EINVAL));
vsize = sp->sem_nsems * sizeof (*vals);
mutex_exit(lock);
vals = kmem_alloc(vsize, KM_SLEEP);
if (copyin((void *)arg, vals, vsize)) {
kmem_free(vals, vsize);
return (set_errno(EFAULT));
}
break;
case IPC_RMID:
if (error = ipc_rmid(sem_svc, semid, cr))
return (set_errno(error));
return (0);
}
if ((lock = ipc_lookup(sem_svc, semid, (kipc_perm_t **)&sp)) == NULL) {
if (vsize != 0)
kmem_free(vals, vsize);
return (set_errno(EINVAL));
}
switch (cmd) {
case IPC_SET:
if (error = ipcperm_set(sem_svc, cr, &sp->sem_perm,
&STRUCT_BUF(sid)->sem_perm, mdl)) {
mutex_exit(lock);
return (set_errno(error));
}
sp->sem_ctime = gethrestime_sec();
mutex_exit(lock);
return (0);
case IPC_STAT:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
ipcperm_stat(&STRUCT_BUF(sid)->sem_perm, &sp->sem_perm, mdl);
STRUCT_FSETP(sid, sem_base, NULL);
STRUCT_FSET(sid, sem_nsems, sp->sem_nsems);
STRUCT_FSET(sid, sem_otime, sp->sem_otime);
STRUCT_FSET(sid, sem_ctime, sp->sem_ctime);
STRUCT_FSET(sid, sem_binary, sp->sem_binary);
mutex_exit(lock);
if (copyout(STRUCT_BUF(sid), (void *)arg, STRUCT_SIZE(sid)))
return (set_errno(EFAULT));
return (0);
case IPC_SET64:
if (error = ipcperm_set64(sem_svc, cr, &sp->sem_perm,
&ds64.semx_perm)) {
mutex_exit(lock);
return (set_errno(error));
}
sp->sem_ctime = gethrestime_sec();
mutex_exit(lock);
return (0);
case IPC_STAT64:
ipcperm_stat64(&ds64.semx_perm, &sp->sem_perm);
ds64.semx_nsems = sp->sem_nsems;
ds64.semx_otime = sp->sem_otime;
ds64.semx_ctime = sp->sem_ctime;
mutex_exit(lock);
if (copyout(&ds64, (void *)arg, sizeof (struct semid_ds64)))
return (set_errno(EFAULT));
return (0);
case GETNCNT:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
if (semnum >= sp->sem_nsems) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
retval = sp->sem_base[semnum].semncnt;
mutex_exit(lock);
return (retval);
case GETPID:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
if (semnum >= sp->sem_nsems) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
retval = sp->sem_base[semnum].sempid;
mutex_exit(lock);
return (retval);
case GETVAL:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
if (semnum >= sp->sem_nsems) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
retval = sp->sem_base[semnum].semval;
mutex_exit(lock);
return (retval);
case GETALL:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
vsize = sp->sem_nsems * sizeof (*vals);
vals = vp = kmem_alloc(vsize, KM_SLEEP);
for (i = sp->sem_nsems, p = sp->sem_base; i--; p++, vp++)
bcopy(&p->semval, vp, sizeof (p->semval));
mutex_exit(lock);
if (copyout((void *)vals, (void *)arg, vsize)) {
kmem_free(vals, vsize);
return (set_errno(EFAULT));
}
kmem_free(vals, vsize);
return (0);
case GETZCNT:
if (error = ipcperm_access(&sp->sem_perm, SEM_R, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
if (semnum >= sp->sem_nsems) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
retval = sp->sem_base[semnum].semzcnt;
mutex_exit(lock);
return (retval);
case SETVAL:
if (error = ipcperm_access(&sp->sem_perm, SEM_A, cr)) {
mutex_exit(lock);
return (set_errno(error));
}
if (semnum >= sp->sem_nsems) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
if ((uint_t)arg > USHRT_MAX) {
mutex_exit(lock);
return (set_errno(ERANGE));
}
p = &sp->sem_base[semnum];
if ((p->semval = (ushort_t)arg) != 0) {
if (p->semncnt) {
cv_broadcast(&p->semncnt_cv);
}
} else if (p->semzcnt) {
cv_broadcast(&p->semzcnt_cv);
}
p->sempid = curproc->p_pid;
sem_undo_clear(sp, (ushort_t)semnum, (ushort_t)semnum);
mutex_exit(lock);
return (0);
case SETALL:
if (sp->sem_nsems * sizeof (*vals) != vsize) {
error = set_errno(EINVAL);
goto seterr;
}
if (error = ipcperm_access(&sp->sem_perm, SEM_A, cr)) {
error = set_errno(error);
goto seterr;
}
sem_undo_clear(sp, 0, sp->sem_nsems - 1);
for (i = 0, p = sp->sem_base; i < sp->sem_nsems;
(p++)->sempid = curproc->p_pid) {
if ((p->semval = vals[i++]) != 0) {
if (p->semncnt) {
cv_broadcast(&p->semncnt_cv);
}
} else if (p->semzcnt) {
cv_broadcast(&p->semzcnt_cv);
}
}
seterr:
mutex_exit(lock);
kmem_free(vals, vsize);
return (error);
default:
mutex_exit(lock);
return (set_errno(EINVAL));
}
}
void
semexit(proc_t *pp)
{
avl_tree_t *tree;
struct sem_undo *undo;
void *cookie = NULL;
mutex_enter(&pp->p_lock);
tree = pp->p_semacct;
pp->p_semacct = NULL;
mutex_exit(&pp->p_lock);
while (undo = avl_destroy_nodes(tree, &cookie)) {
ksemid_t *sp = undo->un_sp;
size_t size = SEM_UNDOSZ(sp->sem_nsems);
int i;
(void) ipc_lock(sem_svc, sp->sem_perm.ipc_id);
if (!IPC_FREE(&sp->sem_perm)) {
for (i = 0; i < sp->sem_nsems; i++) {
int adj = undo->un_aoe[i];
if (adj) {
struct sem *semp = &sp->sem_base[i];
int v = (int)semp->semval + adj;
if (v < 0 || v > USHRT_MAX)
continue;
semp->semval = (ushort_t)v;
if (v == 0 && semp->semzcnt)
cv_broadcast(&semp->semzcnt_cv);
if (adj > 0 && semp->semncnt)
cv_broadcast(&semp->semncnt_cv);
}
}
list_remove(&sp->sem_undos, undo);
}
ipc_rele(sem_svc, (kipc_perm_t *)sp);
kmem_free(undo, size);
}
avl_destroy(tree);
kmem_free(tree, sizeof (avl_tree_t));
}
static void
sem_remove_zone(zoneid_t zoneid, void *arg)
{
ipc_remove_zone(sem_svc, zoneid);
}
static int
semget(key_t key, int nsems, int semflg)
{
ksemid_t *sp;
kmutex_t *lock;
int id, error;
proc_t *pp = curproc;
top:
if (error = ipc_get(sem_svc, key, semflg, (kipc_perm_t **)&sp, &lock))
return (set_errno(error));
if (!IPC_FREE(&sp->sem_perm)) {
if (!((nsems >= 0) && (nsems <= sp->sem_nsems))) {
mutex_exit(lock);
return (set_errno(EINVAL));
}
} else {
if (nsems <= 0 || (rctl_test(rc_process_semmsl, pp->p_rctls, pp,
nsems, RCA_SAFE) & RCT_DENY)) {
mutex_exit(lock);
mutex_exit(&pp->p_lock);
ipc_cleanup(sem_svc, (kipc_perm_t *)sp);
return (set_errno(EINVAL));
}
mutex_exit(lock);
mutex_exit(&pp->p_lock);
sp->sem_base =
kmem_zalloc(P2ROUNDUP(nsems * sizeof (struct sem), 64),
KM_SLEEP);
sp->sem_binary = (nsems == 1);
sp->sem_nsems = (ushort_t)nsems;
sp->sem_ctime = gethrestime_sec();
sp->sem_otime = 0;
list_create(&sp->sem_undos, sizeof (struct sem_undo),
offsetof(struct sem_undo, un_list));
if (error = ipc_commit_begin(sem_svc, key, semflg,
(kipc_perm_t *)sp)) {
if (error == EAGAIN)
goto top;
return (set_errno(error));
}
sp->sem_maxops =
rctl_enforced_value(rc_process_semopm, pp->p_rctls, pp);
if (rctl_test(rc_process_semmsl, pp->p_rctls, pp, nsems,
RCA_SAFE) & RCT_DENY) {
ipc_cleanup(sem_svc, (kipc_perm_t *)sp);
return (set_errno(EINVAL));
}
lock = ipc_commit_end(sem_svc, &sp->sem_perm);
}
if (AU_AUDITING())
audit_ipcget(AT_IPC_SEM, (void *)sp);
id = sp->sem_perm.ipc_id;
mutex_exit(lock);
return (id);
}
static int
semids(int *buf, uint_t nids, uint_t *pnids)
{
int error;
if (error = ipc_ids(sem_svc, buf, nids, pnids))
return (set_errno(error));
return (0);
}
static int
compute_timeout(timespec_t **tsp, timespec_t *ts, timespec_t *now,
timespec_t *timeout)
{
model_t datamodel = get_udatamodel();
if (datamodel == DATAMODEL_NATIVE) {
if (copyin(timeout, ts, sizeof (timespec_t)))
return (EFAULT);
} else {
timespec32_t ts32;
if (copyin(timeout, &ts32, sizeof (timespec32_t)))
return (EFAULT);
TIMESPEC32_TO_TIMESPEC(ts, &ts32)
}
if (itimerspecfix(ts))
return (EINVAL);
timespecadd(ts, now);
*tsp = ts;
return (0);
}
static int
sem_undo_compar(const void *x, const void *y)
{
struct sem_undo *undo1 = (struct sem_undo *)x;
struct sem_undo *undo2 = (struct sem_undo *)y;
if (undo1->un_sp < undo2->un_sp)
return (-1);
if (undo1->un_sp > undo2->un_sp)
return (1);
return (0);
}
static int
sem_undo_alloc(proc_t *pp, ksemid_t *sp, kmutex_t **lock,
struct sem_undo *template, struct sem_undo **un)
{
size_t size;
struct sem_undo *undo;
avl_tree_t *tree = NULL;
avl_index_t where;
mutex_exit(*lock);
size = SEM_UNDOSZ(sp->sem_nsems);
undo = kmem_zalloc(size, KM_SLEEP);
undo->un_proc = pp;
undo->un_sp = sp;
if (pp->p_semacct == NULL)
tree = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP);
*lock = ipc_lock(sem_svc, sp->sem_perm.ipc_id);
if (IPC_FREE(&sp->sem_perm)) {
kmem_free(undo, size);
if (tree)
kmem_free(tree, sizeof (avl_tree_t));
return (EIDRM);
}
mutex_enter(&pp->p_lock);
if (tree) {
if (pp->p_semacct == NULL) {
avl_create(tree, sem_undo_compar,
sizeof (struct sem_undo),
offsetof(struct sem_undo, un_avl));
pp->p_semacct = tree;
} else {
kmem_free(tree, sizeof (avl_tree_t));
}
}
if (*un = avl_find(pp->p_semacct, template, &where)) {
mutex_exit(&pp->p_lock);
kmem_free(undo, size);
} else {
*un = undo;
avl_insert(pp->p_semacct, undo, where);
mutex_exit(&pp->p_lock);
list_insert_head(&sp->sem_undos, undo);
ipc_hold(sem_svc, (kipc_perm_t *)sp);
}
return (0);
}
static int
semop(int semid, struct sembuf *sops, size_t nsops, timespec_t *timeout)
{
ksemid_t *sp = NULL;
kmutex_t *lock;
struct sembuf *op;
int i;
struct sem *semp;
int error = 0;
struct sembuf *uops;
struct sembuf x_sem;
timespec_t now, ts, *tsp = NULL;
int timecheck = 0;
int cvres, needundo, mode;
struct sem_undo *undo;
proc_t *pp = curproc;
int held = 0;
CPU_STATS_ADDQ(CPU, sys, sema, 1);
if (timeout != NULL) {
timecheck = timechanged;
gethrestime(&now);
if (error = compute_timeout(&tsp, &ts, &now, timeout))
return (set_errno(error));
}
if (nsops == 1)
uops = &x_sem;
else if (nsops == 0)
return (0);
else if (nsops <= SEM_MAXUCOPS)
uops = kmem_alloc(nsops * sizeof (*uops), KM_SLEEP);
if (nsops > SEM_MAXUCOPS) {
if ((lock = ipc_lookup(sem_svc, semid,
(kipc_perm_t **)&sp)) == NULL)
return (set_errno(EFAULT));
if (nsops > sp->sem_maxops) {
mutex_exit(lock);
return (set_errno(E2BIG));
}
held = 1;
ipc_hold(sem_svc, (kipc_perm_t *)sp);
mutex_exit(lock);
uops = kmem_alloc(nsops * sizeof (*uops), KM_SLEEP);
if (copyin(sops, uops, nsops * sizeof (*op))) {
error = EFAULT;
(void) ipc_lock(sem_svc, sp->sem_perm.ipc_id);
goto semoperr;
}
lock = ipc_lock(sem_svc, sp->sem_perm.ipc_id);
if (IPC_FREE(&sp->sem_perm)) {
error = EIDRM;
goto semoperr;
}
} else {
if (copyin(sops, uops, nsops * sizeof (*op))) {
error = EFAULT;
goto semoperr_unlocked;
}
if ((lock = ipc_lookup(sem_svc, semid,
(kipc_perm_t **)&sp)) == NULL) {
error = EINVAL;
goto semoperr_unlocked;
}
if (nsops > sp->sem_maxops) {
error = E2BIG;
goto semoperr;
}
}
needundo = 0;
mode = 0;
for (i = 0, op = uops; i++ < nsops; op++) {
mode |= op->sem_op ? SEM_A : SEM_R;
if (op->sem_num >= sp->sem_nsems) {
error = EFBIG;
goto semoperr;
}
if ((op->sem_flg & SEM_UNDO) && op->sem_op)
needundo = 1;
}
if (error = ipcperm_access(&sp->sem_perm, mode, CRED()))
goto semoperr;
if (needundo) {
struct sem_undo template;
template.un_sp = sp;
mutex_enter(&pp->p_lock);
if (pp->p_semacct)
undo = avl_find(pp->p_semacct, &template, NULL);
else
undo = NULL;
mutex_exit(&pp->p_lock);
if (undo == NULL) {
if (!held) {
held = 1;
ipc_hold(sem_svc, (kipc_perm_t *)sp);
}
if (error = sem_undo_alloc(pp, sp, &lock, &template,
&undo))
goto semoperr;
if (error = ipcperm_access(&sp->sem_perm, mode, CRED()))
goto semoperr;
}
}
check:
for (i = 0; i < nsops; i++) {
op = &uops[i];
semp = &sp->sem_base[op->sem_num];
if (op->sem_op > 0) {
if (op->sem_op + (int)semp->semval > USHRT_MAX ||
((op->sem_flg & SEM_UNDO) &&
(error = sem_undo_add(op->sem_op, op->sem_num,
undo)))) {
if (i)
sem_rollback(sp, uops, i, undo);
if (error == 0)
error = ERANGE;
goto semoperr;
}
semp->semval += op->sem_op;
if (semp->semncnt) {
if (op->sem_op == 1 && sp->sem_binary)
cv_signal(&semp->semncnt_cv);
else
cv_broadcast(&semp->semncnt_cv);
}
if (semp->semzcnt && !semp->semval)
cv_broadcast(&semp->semzcnt_cv);
continue;
}
if (op->sem_op < 0) {
if (semp->semval >= (unsigned)(-op->sem_op)) {
if ((op->sem_flg & SEM_UNDO) &&
(error = sem_undo_add(op->sem_op,
op->sem_num, undo))) {
if (i)
sem_rollback(sp, uops, i, undo);
goto semoperr;
}
semp->semval += op->sem_op;
if (semp->semzcnt && !semp->semval)
cv_broadcast(&semp->semzcnt_cv);
continue;
}
if (i)
sem_rollback(sp, uops, i, undo);
if (op->sem_flg & IPC_NOWAIT) {
error = EAGAIN;
goto semoperr;
}
if (op->sem_op < -1)
sp->sem_binary = 0;
if (!held) {
held = 1;
ipc_hold(sem_svc, (kipc_perm_t *)sp);
}
semp->semncnt++;
cvres = cv_waituntil_sig(&semp->semncnt_cv, lock,
tsp, timecheck);
lock = ipc_relock(sem_svc, sp->sem_perm.ipc_id, lock);
if (!IPC_FREE(&sp->sem_perm)) {
ASSERT(semp->semncnt != 0);
semp->semncnt--;
if (cvres > 0)
goto check;
}
if (cvres == 0)
error = EINTR;
else if (cvres < 0)
error = EAGAIN;
else
error = EIDRM;
goto semoperr;
}
if (semp->semval) {
if (i)
sem_rollback(sp, uops, i, undo);
if (op->sem_flg & IPC_NOWAIT) {
error = EAGAIN;
goto semoperr;
}
if (!held) {
held = 1;
ipc_hold(sem_svc, (kipc_perm_t *)sp);
}
semp->semzcnt++;
cvres = cv_waituntil_sig(&semp->semzcnt_cv, lock,
tsp, timecheck);
lock = ipc_relock(sem_svc, sp->sem_perm.ipc_id, lock);
if (!IPC_FREE(&sp->sem_perm)) {
ASSERT(semp->semzcnt != 0);
semp->semzcnt--;
if (cvres > 0)
goto check;
}
if (cvres == 0)
error = EINTR;
else if (cvres < 0)
error = EAGAIN;
else
error = EIDRM;
goto semoperr;
}
}
for (i = 0, op = uops; i++ < nsops;
sp->sem_base[(op++)->sem_num].sempid = pp->p_pid)
;
sp->sem_otime = gethrestime_sec();
if (held)
ipc_rele(sem_svc, (kipc_perm_t *)sp);
else
mutex_exit(lock);
if (nsops != 1)
kmem_free(uops, sizeof (*uops) * nsops);
return (0);
semoperr:
if (held)
ipc_rele(sem_svc, (kipc_perm_t *)sp);
else
mutex_exit(lock);
semoperr_unlocked:
if (nsops != 1)
kmem_free(uops, sizeof (*uops) * nsops);
return (set_errno(error));
}
static int
semsys(int opcode, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4)
{
int error;
switch (opcode) {
case SEMCTL:
error = semctl((int)a1, (uint_t)a2, (int)a3, a4);
break;
case SEMGET:
error = semget((key_t)a1, (int)a2, (int)a3);
break;
case SEMOP:
error = semop((int)a1, (struct sembuf *)a2, (size_t)a3, 0);
break;
case SEMIDS:
error = semids((int *)a1, (uint_t)a2, (uint_t *)a3);
break;
case SEMTIMEDOP:
error = semop((int)a1, (struct sembuf *)a2, (size_t)a3,
(timespec_t *)a4);
break;
default:
error = set_errno(EINVAL);
break;
}
return (error);
}