#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/prsystm.h>
#include <sys/kmem.h>
#include <sys/sobject.h>
#include <sys/fault.h>
#include <sys/procfs.h>
#include <sys/watchpoint.h>
#include <sys/time.h>
#include <sys/cmn_err.h>
#include <sys/machlock.h>
#include <sys/debug.h>
#include <sys/synch.h>
#include <sys/synch32.h>
#include <sys/mman.h>
#include <sys/class.h>
#include <sys/schedctl.h>
#include <sys/sleepq.h>
#include <sys/policy.h>
#include <sys/lwpchan_impl.h>
#include <sys/turnstile.h>
#include <sys/atomic.h>
#include <sys/lwp_timer_impl.h>
#include <sys/lwp_upimutex_impl.h>
#include <vm/as.h>
#include <sys/sdt.h>
static kthread_t *lwpsobj_owner(caddr_t);
static void lwp_unsleep(kthread_t *t);
static void lwp_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip);
static void lwp_mutex_cleanup(lwpchan_entry_t *ent, uint16_t lockflg);
static void lwp_mutex_unregister(void *uaddr);
static void set_owner_pid(lwp_mutex_t *, uintptr_t, pid_t);
static int iswanted(kthread_t *, lwpchan_t *);
extern int lwp_cond_signal(lwp_cond_t *cv);
static uint32_t maxnestupimx = 2000;
static sobj_ops_t lwp_sobj_ops = {
SOBJ_USER, lwpsobj_owner, lwp_unsleep, lwp_change_pri
};
static kthread_t *lwpsobj_pi_owner(upimutex_t *up);
static sobj_ops_t lwp_sobj_pi_ops = {
SOBJ_USER_PI, lwpsobj_pi_owner, turnstile_unsleep,
turnstile_change_pri
};
static sleepq_head_t lwpsleepq[NSLEEPQ];
upib_t upimutextab[UPIMUTEX_TABSIZE];
#define LWPCHAN_LOCK_SHIFT 10
#define LWPCHAN_LOCK_SIZE (1 << LWPCHAN_LOCK_SHIFT)
#define LWPCHAN_LOCK_HASH(X, pool) \
(((((X) >> 3) ^ ((X) >> (LWPCHAN_LOCK_SHIFT + 3))) & \
(LWPCHAN_LOCK_SIZE - 1)) + ((pool)? LWPCHAN_LOCK_SIZE : 0))
static kmutex_t lwpchanlock[2 * LWPCHAN_LOCK_SIZE];
#define UPIMUTEX(type) ((type) & LOCK_PRIO_INHERIT)
static sleepq_head_t *
lwpsqhash(lwpchan_t *lwpchan)
{
uint_t x = (uintptr_t)lwpchan->lc_wchan ^ (uintptr_t)lwpchan->lc_wchan0;
return (&lwpsleepq[SQHASHINDEX(x)]);
}
static void
lwpchan_lock(lwpchan_t *lwpchan, int pool)
{
uint_t x = (uintptr_t)lwpchan->lc_wchan ^ (uintptr_t)lwpchan->lc_wchan0;
mutex_enter(&lwpchanlock[LWPCHAN_LOCK_HASH(x, pool)]);
}
static void
lwpchan_unlock(lwpchan_t *lwpchan, int pool)
{
uint_t x = (uintptr_t)lwpchan->lc_wchan ^ (uintptr_t)lwpchan->lc_wchan0;
mutex_exit(&lwpchanlock[LWPCHAN_LOCK_HASH(x, pool)]);
}
void
lwpchan_delete_mapping(proc_t *p, caddr_t start, caddr_t end)
{
lwpchan_data_t *lcp;
lwpchan_hashbucket_t *hashbucket;
lwpchan_hashbucket_t *endbucket;
lwpchan_entry_t *ent;
lwpchan_entry_t **prev;
caddr_t addr;
mutex_enter(&p->p_lcp_lock);
lcp = p->p_lcp;
hashbucket = lcp->lwpchan_cache;
endbucket = hashbucket + lcp->lwpchan_size;
for (; hashbucket < endbucket; hashbucket++) {
if (hashbucket->lwpchan_chain == NULL)
continue;
mutex_enter(&hashbucket->lwpchan_lock);
prev = &hashbucket->lwpchan_chain;
while ((ent = *prev) != NULL) {
addr = ent->lwpchan_addr;
if (start <= addr && addr < end) {
*prev = ent->lwpchan_next;
if (ent->lwpchan_pool == LWPCHAN_MPPOOL &&
(ent->lwpchan_type & USYNC_PROCESS_ROBUST))
lwp_mutex_cleanup(ent, LOCK_UNMAPPED);
if ((addr = ent->lwpchan_uaddr) != NULL)
lwp_mutex_unregister(addr);
kmem_free(ent, sizeof (*ent));
atomic_dec_32(&lcp->lwpchan_entries);
} else {
prev = &ent->lwpchan_next;
}
}
mutex_exit(&hashbucket->lwpchan_lock);
}
mutex_exit(&p->p_lcp_lock);
}
static lwpchan_hashbucket_t *
lwpchan_bucket(lwpchan_data_t *lcp, uintptr_t addr)
{
uint_t i;
addr >>= 3;
i = (addr ^ (addr >> lcp->lwpchan_bits)) & lcp->lwpchan_mask;
return (lcp->lwpchan_cache + i);
}
static void
lwpchan_alloc_cache(proc_t *p, uint_t bits)
{
lwpchan_data_t *lcp;
lwpchan_data_t *old_lcp;
lwpchan_hashbucket_t *hashbucket;
lwpchan_hashbucket_t *endbucket;
lwpchan_hashbucket_t *newbucket;
lwpchan_entry_t *ent;
lwpchan_entry_t *next;
uint_t count;
ASSERT(bits >= LWPCHAN_INITIAL_BITS && bits <= LWPCHAN_MAX_BITS);
lcp = kmem_alloc(sizeof (lwpchan_data_t), KM_SLEEP);
lcp->lwpchan_bits = bits;
lcp->lwpchan_size = 1 << lcp->lwpchan_bits;
lcp->lwpchan_mask = lcp->lwpchan_size - 1;
lcp->lwpchan_entries = 0;
lcp->lwpchan_cache = kmem_zalloc(lcp->lwpchan_size *
sizeof (lwpchan_hashbucket_t), KM_SLEEP);
lcp->lwpchan_next_data = NULL;
mutex_enter(&p->p_lcp_lock);
if ((old_lcp = p->p_lcp) != NULL) {
if (old_lcp->lwpchan_bits >= bits) {
mutex_exit(&p->p_lcp_lock);
kmem_free(lcp->lwpchan_cache, lcp->lwpchan_size *
sizeof (lwpchan_hashbucket_t));
kmem_free(lcp, sizeof (lwpchan_data_t));
return;
}
hashbucket = old_lcp->lwpchan_cache;
endbucket = hashbucket + old_lcp->lwpchan_size;
for (; hashbucket < endbucket; hashbucket++)
mutex_enter(&hashbucket->lwpchan_lock);
count = 0;
hashbucket = old_lcp->lwpchan_cache;
for (; hashbucket < endbucket; hashbucket++) {
ent = hashbucket->lwpchan_chain;
while (ent != NULL) {
next = ent->lwpchan_next;
newbucket = lwpchan_bucket(lcp,
(uintptr_t)ent->lwpchan_addr);
ent->lwpchan_next = newbucket->lwpchan_chain;
newbucket->lwpchan_chain = ent;
ent = next;
count++;
}
hashbucket->lwpchan_chain = NULL;
}
lcp->lwpchan_entries = count;
}
lcp->lwpchan_next_data = old_lcp;
membar_producer();
p->p_lcp = lcp;
if (old_lcp != NULL) {
hashbucket = old_lcp->lwpchan_cache;
for (; hashbucket < endbucket; hashbucket++)
mutex_exit(&hashbucket->lwpchan_lock);
}
mutex_exit(&p->p_lcp_lock);
}
void
lwpchan_destroy_cache(int exec)
{
proc_t *p = curproc;
lwpchan_hashbucket_t *hashbucket;
lwpchan_hashbucket_t *endbucket;
lwpchan_data_t *lcp;
lwpchan_entry_t *ent;
lwpchan_entry_t *next;
uint16_t lockflg;
lcp = p->p_lcp;
p->p_lcp = NULL;
lockflg = exec? LOCK_UNMAPPED : LOCK_OWNERDEAD;
hashbucket = lcp->lwpchan_cache;
endbucket = hashbucket + lcp->lwpchan_size;
for (; hashbucket < endbucket; hashbucket++) {
ent = hashbucket->lwpchan_chain;
hashbucket->lwpchan_chain = NULL;
while (ent != NULL) {
next = ent->lwpchan_next;
if (ent->lwpchan_pool == LWPCHAN_MPPOOL &&
(ent->lwpchan_type & (USYNC_PROCESS | LOCK_ROBUST))
== (USYNC_PROCESS | LOCK_ROBUST))
lwp_mutex_cleanup(ent, lockflg);
kmem_free(ent, sizeof (*ent));
ent = next;
}
}
while (lcp != NULL) {
lwpchan_data_t *next_lcp = lcp->lwpchan_next_data;
kmem_free(lcp->lwpchan_cache, lcp->lwpchan_size *
sizeof (lwpchan_hashbucket_t));
kmem_free(lcp, sizeof (lwpchan_data_t));
lcp = next_lcp;
}
}
static uint_t
lwpchan_cache_mapping(caddr_t addr, int type, int pool, lwpchan_t *lwpchan,
lwpchan_hashbucket_t *hashbucket)
{
lwpchan_entry_t *ent;
uint_t count = 1;
for (ent = hashbucket->lwpchan_chain; ent; ent = ent->lwpchan_next) {
if (ent->lwpchan_addr == addr) {
if (ent->lwpchan_type != type ||
ent->lwpchan_pool != pool) {
ent->lwpchan_type = (uint16_t)type;
ent->lwpchan_pool = (uint16_t)pool;
}
*lwpchan = ent->lwpchan_lwpchan;
return (0);
}
count++;
}
return (count);
}
static int
lwpchan_get_mapping(struct as *as, caddr_t addr, caddr_t uaddr,
int type, lwpchan_t *lwpchan, int pool)
{
proc_t *p = curproc;
lwpchan_data_t *lcp;
lwpchan_hashbucket_t *hashbucket;
lwpchan_entry_t *ent;
memid_t memid;
uint_t count;
uint_t bits;
top:
if ((lcp = p->p_lcp) == NULL) {
lwpchan_alloc_cache(p, LWPCHAN_INITIAL_BITS);
goto top;
}
hashbucket = lwpchan_bucket(lcp, (uintptr_t)addr);
mutex_enter(&hashbucket->lwpchan_lock);
if (lcp != p->p_lcp) {
mutex_exit(&hashbucket->lwpchan_lock);
goto top;
}
if (lwpchan_cache_mapping(addr, type, pool, lwpchan, hashbucket) == 0) {
mutex_exit(&hashbucket->lwpchan_lock);
return (1);
}
mutex_exit(&hashbucket->lwpchan_lock);
if (as_getmemid(as, addr, &memid) != 0)
return (0);
lwpchan->lc_wchan0 = (caddr_t)(uintptr_t)memid.val[0];
lwpchan->lc_wchan = (caddr_t)(uintptr_t)memid.val[1];
ent = kmem_alloc(sizeof (lwpchan_entry_t), KM_SLEEP);
mutex_enter(&hashbucket->lwpchan_lock);
if (lcp != p->p_lcp) {
mutex_exit(&hashbucket->lwpchan_lock);
kmem_free(ent, sizeof (*ent));
goto top;
}
count = lwpchan_cache_mapping(addr, type, pool, lwpchan, hashbucket);
if (count == 0) {
mutex_exit(&hashbucket->lwpchan_lock);
kmem_free(ent, sizeof (*ent));
return (1);
}
if (count > lcp->lwpchan_bits + 2 &&
(bits = lcp->lwpchan_bits) < LWPCHAN_MAX_BITS) {
mutex_exit(&hashbucket->lwpchan_lock);
kmem_free(ent, sizeof (*ent));
lwpchan_alloc_cache(p, bits + 1);
goto top;
}
ent->lwpchan_addr = addr;
ent->lwpchan_uaddr = uaddr;
ent->lwpchan_type = (uint16_t)type;
ent->lwpchan_pool = (uint16_t)pool;
ent->lwpchan_lwpchan = *lwpchan;
ent->lwpchan_next = hashbucket->lwpchan_chain;
hashbucket->lwpchan_chain = ent;
atomic_inc_32(&lcp->lwpchan_entries);
mutex_exit(&hashbucket->lwpchan_lock);
return (1);
}
static int
get_lwpchan(struct as *as, caddr_t addr, int type, lwpchan_t *lwpchan, int pool)
{
if (!(type & USYNC_PROCESS)) {
lwpchan->lc_wchan0 = (caddr_t)as;
lwpchan->lc_wchan = addr;
return (1);
}
return (lwpchan_get_mapping(as, addr, NULL, type, lwpchan, pool));
}
static void
lwp_block(lwpchan_t *lwpchan)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
sleepq_head_t *sqh;
thread_lock(t);
t->t_flag |= T_WAKEABLE;
t->t_lwpchan = *lwpchan;
t->t_sobj_ops = &lwp_sobj_ops;
t->t_release = 0;
sqh = lwpsqhash(lwpchan);
disp_lock_enter_high(&sqh->sq_lock);
CL_SLEEP(t);
DTRACE_SCHED(sleep);
THREAD_SLEEP(t, &sqh->sq_lock);
sleepq_insert(&sqh->sq_queue, t);
thread_unlock(t);
lwp->lwp_asleep = 1;
lwp->lwp_sysabort = 0;
lwp->lwp_ru.nvcsw++;
(void) new_mstate(curthread, LMS_SLEEP);
}
static kthread_t *
lwpsobj_pi_owner(upimutex_t *up)
{
return (up->upi_owner);
}
static struct upimutex *
upi_get(upib_t *upibp, lwpchan_t *lcp)
{
struct upimutex *upip;
for (upip = upibp->upib_first; upip != NULL;
upip = upip->upi_nextchain) {
if (upip->upi_lwpchan.lc_wchan0 == lcp->lc_wchan0 &&
upip->upi_lwpchan.lc_wchan == lcp->lc_wchan)
break;
}
return (upip);
}
static void
upi_chain_add(upib_t *upibp, struct upimutex *upimutex)
{
ASSERT(MUTEX_HELD(&upibp->upib_lock));
upimutex->upi_nextchain = upibp->upib_first;
upibp->upib_first = upimutex;
}
static void
upi_chain_del(upib_t *upibp, struct upimutex *upimutex)
{
struct upimutex **prev;
ASSERT(MUTEX_HELD(&upibp->upib_lock));
prev = &upibp->upib_first;
while (*prev != upimutex) {
prev = &(*prev)->upi_nextchain;
}
*prev = upimutex->upi_nextchain;
upimutex->upi_nextchain = NULL;
}
static uint32_t
upi_mylist_add(struct upimutex *upimutex)
{
kthread_t *t = curthread;
upimutex->upi_nextowned = t->t_upimutex;
t->t_upimutex = upimutex;
t->t_nupinest++;
ASSERT(t->t_nupinest > 0);
return (t->t_nupinest);
}
static void
upi_mylist_del(struct upimutex *upimutex)
{
kthread_t *t = curthread;
struct upimutex **prev;
prev = &t->t_upimutex;
while (*prev != upimutex) {
prev = &(*prev)->upi_nextowned;
}
*prev = upimutex->upi_nextowned;
upimutex->upi_nextowned = NULL;
ASSERT(t->t_nupinest > 0);
t->t_nupinest--;
}
static int
upi_owned(upimutex_t *upim)
{
return (upim->upi_owner == curthread);
}
static struct upimutex *
lwp_upimutex_owned(lwp_mutex_t *lp, uint8_t type)
{
lwpchan_t lwpchan;
upib_t *upibp;
struct upimutex *upimutex;
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL))
return (NULL);
upibp = &UPI_CHAIN(lwpchan);
mutex_enter(&upibp->upib_lock);
upimutex = upi_get(upibp, &lwpchan);
if (upimutex == NULL || upimutex->upi_owner != curthread) {
mutex_exit(&upibp->upib_lock);
return (NULL);
}
mutex_exit(&upibp->upib_lock);
return (upimutex);
}
static void
upimutex_unlock(struct upimutex *upimutex, uint16_t flag)
{
turnstile_t *ts;
upib_t *upibp;
kthread_t *newowner;
upi_mylist_del(upimutex);
upibp = upimutex->upi_upibp;
mutex_enter(&upibp->upib_lock);
if (upimutex->upi_waiter != 0) {
ts = turnstile_lookup(upimutex);
if (ts != NULL && !(flag & LOCK_NOTRECOVERABLE)) {
newowner = ts->ts_sleepq[TS_WRITER_Q].sq_first;
upimutex->upi_owner = newowner;
if (ts->ts_waiters == 1)
upimutex->upi_waiter = 0;
turnstile_wakeup(ts, TS_WRITER_Q, 1, newowner);
mutex_exit(&upibp->upib_lock);
return;
} else if (ts != NULL) {
turnstile_wakeup(ts, TS_WRITER_Q, ts->ts_waiters, NULL);
} else {
turnstile_exit(upimutex);
turnstile_pi_recalc();
}
}
upi_chain_del(upimutex->upi_upibp, upimutex);
mutex_exit(&upibp->upib_lock);
kmem_free(upimutex, sizeof (upimutex_t));
}
static int
lwp_upimutex_lock(lwp_mutex_t *lp, uint8_t type, int try, lwp_timer_t *lwptp)
{
label_t ljb;
int error = 0;
lwpchan_t lwpchan;
uint16_t flag;
upib_t *upibp;
volatile struct upimutex *upimutex = NULL;
turnstile_t *ts;
uint32_t nupinest;
volatile int upilocked = 0;
if (on_fault(&ljb)) {
if (upilocked)
upimutex_unlock((upimutex_t *)upimutex, 0);
error = EFAULT;
goto out;
}
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
upibp = &UPI_CHAIN(lwpchan);
retry:
mutex_enter(&upibp->upib_lock);
upimutex = upi_get(upibp, &lwpchan);
if (upimutex == NULL) {
upimutex = kmem_zalloc(sizeof (upimutex_t), KM_SLEEP);
upi_chain_add(upibp, (upimutex_t *)upimutex);
upimutex->upi_owner = curthread;
upimutex->upi_upibp = upibp;
upimutex->upi_vaddr = lp;
upimutex->upi_lwpchan = lwpchan;
mutex_exit(&upibp->upib_lock);
nupinest = upi_mylist_add((upimutex_t *)upimutex);
upilocked = 1;
fuword16_noerr(&lp->mutex_flag, &flag);
if (nupinest > maxnestupimx &&
secpolicy_resource(CRED()) != 0) {
upimutex_unlock((upimutex_t *)upimutex, flag);
error = ENOMEM;
goto out;
}
if (flag & LOCK_NOTRECOVERABLE) {
upimutex_unlock((upimutex_t *)upimutex, flag);
upilocked = 0;
error = ENOTRECOVERABLE;
} else if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
if (flag & LOCK_OWNERDEAD)
error = EOWNERDEAD;
else if (type & USYNC_PROCESS_ROBUST)
error = ELOCKUNMAPPED;
else
error = EOWNERDEAD;
}
goto out;
}
ASSERT(upimutex->upi_owner != NULL);
if (upimutex->upi_owner == curthread) {
mutex_exit(&upibp->upib_lock);
error = EDEADLK;
goto out;
}
if (try == UPIMUTEX_TRY) {
mutex_exit(&upibp->upib_lock);
error = EBUSY;
goto out;
}
if ((error = lwptp->lwpt_time_error) != 0) {
mutex_exit(&upibp->upib_lock);
goto out;
}
if (lwptp->lwpt_tsp != NULL) {
mutex_enter(&curthread->t_delay_lock);
(void) lwp_timer_enqueue(lwptp);
mutex_exit(&curthread->t_delay_lock);
}
ts = turnstile_lookup((upimutex_t *)upimutex);
upimutex->upi_waiter = 1;
error = turnstile_block(ts, TS_WRITER_Q, (upimutex_t *)upimutex,
&lwp_sobj_pi_ops, &upibp->upib_lock, lwptp);
if (error != 0) {
if ((error == EINTR || error == ETIME) &&
(upimutex = lwp_upimutex_owned(lp, type))) {
(void) upi_mylist_add((upimutex_t *)upimutex);
upimutex_unlock((upimutex_t *)upimutex, 0);
}
ASSERT(error == EINTR || error == ETIME ||
(error == EDEADLK && !upi_owned((upimutex_t *)upimutex)));
ASSERT(!lwp_upimutex_owned(lp, type));
goto out;
}
if (lwp_upimutex_owned(lp, type)) {
ASSERT(lwp_upimutex_owned(lp, type) == upimutex);
nupinest = upi_mylist_add((upimutex_t *)upimutex);
upilocked = 1;
}
fuword16_noerr(&lp->mutex_flag, &flag);
if (upilocked) {
if (nupinest > maxnestupimx &&
secpolicy_resource(CRED()) != 0) {
upimutex_unlock((upimutex_t *)upimutex, flag);
upilocked = 0;
error = ENOMEM;
} else if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
if (flag & LOCK_OWNERDEAD)
error = EOWNERDEAD;
else if (type & USYNC_PROCESS_ROBUST)
error = ELOCKUNMAPPED;
else
error = EOWNERDEAD;
}
} else {
if (flag & LOCK_NOTRECOVERABLE) {
error = ENOTRECOVERABLE;
} else {
goto retry;
}
}
out:
no_fault();
return (error);
}
static int
lwp_upimutex_unlock(lwp_mutex_t *lp, uint8_t type)
{
label_t ljb;
int error = 0;
lwpchan_t lwpchan;
uint16_t flag;
upib_t *upibp;
volatile struct upimutex *upimutex = NULL;
volatile int upilocked = 0;
if (on_fault(&ljb)) {
if (upilocked)
upimutex_unlock((upimutex_t *)upimutex, 0);
error = EFAULT;
goto out;
}
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
upibp = &UPI_CHAIN(lwpchan);
mutex_enter(&upibp->upib_lock);
upimutex = upi_get(upibp, &lwpchan);
if (upimutex == NULL || upimutex->upi_owner != curthread) {
mutex_exit(&upibp->upib_lock);
error = EPERM;
goto out;
}
mutex_exit(&upibp->upib_lock);
upilocked = 1;
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
flag &= ~(LOCK_OWNERDEAD | LOCK_UNMAPPED);
flag |= LOCK_NOTRECOVERABLE;
suword16_noerr(&lp->mutex_flag, flag);
}
set_owner_pid(lp, 0, 0);
upimutex_unlock((upimutex_t *)upimutex, flag);
upilocked = 0;
out:
no_fault();
return (error);
}
static void
set_owner_pid(lwp_mutex_t *lp, uintptr_t owner, pid_t pid)
{
union {
uint64_t word64;
uint32_t word32[2];
} un;
un.word64 = (uint64_t)owner;
suword32_noerr(&lp->mutex_ownerpid, pid);
#if defined(_LP64)
if (((uintptr_t)lp & (_LONG_LONG_ALIGNMENT - 1)) == 0) {
suword64_noerr(&lp->mutex_owner, un.word64);
return;
}
#endif
suword32_noerr((uint32_t *)&lp->mutex_owner, un.word32[0]);
suword32_noerr((uint32_t *)&lp->mutex_owner + 1, un.word32[1]);
}
static uint16_t
lwp_clear_mutex(lwp_mutex_t *lp, uint16_t lockflg)
{
uint16_t flag;
fuword16_noerr(&lp->mutex_flag, &flag);
if ((flag &
(LOCK_OWNERDEAD | LOCK_UNMAPPED | LOCK_NOTRECOVERABLE)) == 0) {
flag |= lockflg;
suword16_noerr(&lp->mutex_flag, flag);
}
set_owner_pid(lp, 0, 0);
suword8_noerr(&lp->mutex_rcount, 0);
return (flag);
}
static int
upi_dead(upimutex_t *upip, uint16_t lockflg)
{
label_t ljb;
int error = 0;
lwp_mutex_t *lp;
if (on_fault(&ljb)) {
error = EFAULT;
goto out;
}
lp = upip->upi_vaddr;
(void) lwp_clear_mutex(lp, lockflg);
suword8_noerr(&lp->mutex_lockw, 0);
out:
no_fault();
return (error);
}
void
upimutex_cleanup()
{
kthread_t *t = curthread;
uint16_t lockflg = (ttoproc(t)->p_proc_flag & P_PR_EXEC)?
LOCK_UNMAPPED : LOCK_OWNERDEAD;
struct upimutex *upip;
while ((upip = t->t_upimutex) != NULL) {
if (upi_dead(upip, lockflg) != 0) {
upimutex_unlock(upip, LOCK_NOTRECOVERABLE);
} else {
upimutex_unlock(upip, 0);
}
}
}
int
lwp_mutex_timedlock(lwp_mutex_t *lp, timespec_t *tsp, uintptr_t owner)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
proc_t *p = ttoproc(t);
lwp_timer_t lwpt;
caddr_t timedwait;
int error = 0;
int time_error;
clock_t tim = -1;
uchar_t waiters;
volatile int locked = 0;
volatile int watched = 0;
label_t ljb;
volatile uint8_t type = 0;
lwpchan_t lwpchan;
sleepq_head_t *sqh;
uint16_t flag;
int imm_timeout = 0;
if ((caddr_t)lp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
prstop(PR_REQUESTED, 0);
timedwait = (caddr_t)tsp;
if ((time_error = lwp_timer_copyin(&lwpt, tsp)) == 0 &&
lwpt.lwpt_imm_timeout) {
imm_timeout = 1;
timedwait = NULL;
}
(void) new_mstate(t, LMS_USER_LOCK);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = EFAULT;
goto out;
}
fuword8_noerr(&lp->mutex_type, (uint8_t *)&type);
suword8_noerr(&lp->mutex_type, type);
if (UPIMUTEX(type)) {
no_fault();
error = lwp_upimutex_lock(lp, type, UPIMUTEX_BLOCK, &lwpt);
if (error == 0 || error == EOWNERDEAD ||
error == ELOCKUNMAPPED) {
volatile int locked = error != 0;
if (on_fault(&ljb)) {
if (locked != 0)
error = lwp_upimutex_unlock(lp, type);
else
error = EFAULT;
goto upierr;
}
set_owner_pid(lp, owner,
(type & USYNC_PROCESS)? p->p_pid : 0);
no_fault();
}
upierr:
if (tsp && !time_error)
error = lwp_timer_copyout(&lwpt, error);
if (error)
return (set_errno(error));
return (0);
}
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & LOCK_NOTRECOVERABLE) {
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = ENOTRECOVERABLE;
goto out;
}
}
fuword8_noerr(&lp->mutex_waiters, &waiters);
suword8_noerr(&lp->mutex_waiters, 1);
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
while (!ulock_try(&lp->mutex_lockw)) {
if (time_error) {
error = time_error;
break;
}
if (watched) {
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
watched = 0;
}
if (timedwait) {
mutex_enter(&t->t_delay_lock);
if (lwp_timer_enqueue(&lwpt) != 0) {
mutex_exit(&t->t_delay_lock);
imm_timeout = 1;
timedwait = NULL;
}
}
lwp_block(&lwpchan);
if (timedwait)
mutex_exit(&t->t_delay_lock);
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) || imm_timeout)
setrun(t);
swtch();
t->t_flag &= ~T_WAKEABLE;
if (timedwait)
tim = lwp_timer_dequeue(&lwpt);
setallwatch();
if (ISSIG(t, FORREAL) || lwp->lwp_sysabort || MUSTRETURN(p, t))
error = EINTR;
else if (imm_timeout || (timedwait && tim == -1))
error = ETIME;
if (error) {
lwp->lwp_asleep = 0;
lwp->lwp_sysabort = 0;
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp),
S_WRITE);
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
sqh = lwpsqhash(&lwpchan);
disp_lock_enter(&sqh->sq_lock);
waiters = iswanted(sqh->sq_queue.sq_first, &lwpchan);
disp_lock_exit(&sqh->sq_lock);
break;
}
lwp->lwp_asleep = 0;
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp),
S_WRITE);
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
fuword8_noerr(&lp->mutex_waiters, &waiters);
suword8_noerr(&lp->mutex_waiters, 1);
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & LOCK_NOTRECOVERABLE) {
error = ENOTRECOVERABLE;
break;
}
}
}
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
if (error == 0) {
set_owner_pid(lp, owner, (type & USYNC_PROCESS)? p->p_pid : 0);
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
if (flag & LOCK_OWNERDEAD)
error = EOWNERDEAD;
else if (type & USYNC_PROCESS_ROBUST)
error = ELOCKUNMAPPED;
else
error = EOWNERDEAD;
}
}
}
suword8_noerr(&lp->mutex_waiters, waiters);
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (tsp && !time_error)
error = lwp_timer_copyout(&lwpt, error);
if (error)
return (set_errno(error));
return (0);
}
static int
iswanted(kthread_t *t, lwpchan_t *lwpchan)
{
while (t != NULL) {
if (t->t_lwpchan.lc_wchan0 == lwpchan->lc_wchan0 &&
t->t_lwpchan.lc_wchan == lwpchan->lc_wchan)
return (1);
t = t->t_link;
}
return (0);
}
static kthread_t *
lwp_queue_waiter(lwpchan_t *lwpchan)
{
sleepq_head_t *sqh;
kthread_t *tp;
sqh = lwpsqhash(lwpchan);
disp_lock_enter(&sqh->sq_lock);
for (tp = sqh->sq_queue.sq_first; tp != NULL; tp = tp->t_link) {
if (tp->t_lwpchan.lc_wchan0 == lwpchan->lc_wchan0 &&
tp->t_lwpchan.lc_wchan == lwpchan->lc_wchan)
break;
}
disp_lock_exit(&sqh->sq_lock);
return (tp);
}
static int
lwp_release(lwpchan_t *lwpchan, uchar_t *waiters, int sync_type)
{
sleepq_head_t *sqh;
kthread_t *tp;
kthread_t **tpp;
sqh = lwpsqhash(lwpchan);
disp_lock_enter(&sqh->sq_lock);
tpp = &sqh->sq_queue.sq_first;
while ((tp = *tpp) != NULL) {
if (tp->t_lwpchan.lc_wchan0 == lwpchan->lc_wchan0 &&
tp->t_lwpchan.lc_wchan == lwpchan->lc_wchan) {
if (sync_type != (tp->t_flag & T_WAITCVSEM)) {
ASSERT(sync_type == 0);
disp_lock_exit(&sqh->sq_lock);
return (0);
}
*waiters = iswanted(tp->t_link, lwpchan);
sleepq_unlink(tpp, tp);
DTRACE_SCHED1(wakeup, kthread_t *, tp);
tp->t_wchan0 = NULL;
tp->t_wchan = NULL;
tp->t_sobj_ops = NULL;
tp->t_release = 1;
THREAD_TRANSITION(tp);
CL_WAKEUP(tp);
thread_unlock(tp);
return (1);
}
tpp = &tp->t_link;
}
*waiters = 0;
disp_lock_exit(&sqh->sq_lock);
return (0);
}
static void
lwp_release_all(lwpchan_t *lwpchan)
{
sleepq_head_t *sqh;
kthread_t *tp;
kthread_t **tpp;
sqh = lwpsqhash(lwpchan);
disp_lock_enter(&sqh->sq_lock);
tpp = &sqh->sq_queue.sq_first;
while ((tp = *tpp) != NULL) {
if (tp->t_lwpchan.lc_wchan0 == lwpchan->lc_wchan0 &&
tp->t_lwpchan.lc_wchan == lwpchan->lc_wchan) {
sleepq_unlink(tpp, tp);
DTRACE_SCHED1(wakeup, kthread_t *, tp);
tp->t_wchan0 = NULL;
tp->t_wchan = NULL;
tp->t_sobj_ops = NULL;
CL_WAKEUP(tp);
thread_unlock_high(tp);
} else {
tpp = &tp->t_link;
}
}
disp_lock_exit(&sqh->sq_lock);
}
int
lwp_mutex_wakeup(lwp_mutex_t *lp, int release_all)
{
proc_t *p = ttoproc(curthread);
lwpchan_t lwpchan;
uchar_t waiters;
volatile int locked = 0;
volatile int watched = 0;
volatile uint8_t type = 0;
label_t ljb;
int error = 0;
if ((caddr_t)lp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = EFAULT;
goto out;
}
fuword8_noerr(&lp->mutex_type, (uint8_t *)&type);
suword8_noerr(&lp->mutex_type, type);
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
if (release_all)
lwp_release_all(&lwpchan);
else if (lwp_release(&lwpchan, &waiters, 0))
suword8_noerr(&lp->mutex_waiters, waiters);
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_cond_wait(lwp_cond_t *cv, lwp_mutex_t *mp, timespec_t *tsp, int check_park)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
proc_t *p = ttoproc(t);
lwp_timer_t lwpt;
lwpchan_t cv_lwpchan;
lwpchan_t m_lwpchan;
caddr_t timedwait;
volatile uint16_t type = 0;
volatile uint8_t mtype = 0;
uchar_t waiters;
volatile int error;
clock_t tim = -1;
volatile int locked = 0;
volatile int m_locked = 0;
volatile int cvwatched = 0;
volatile int mpwatched = 0;
label_t ljb;
volatile int no_lwpchan = 1;
int imm_timeout = 0;
int imm_unpark = 0;
if ((caddr_t)cv >= p->p_as->a_userlimit ||
(caddr_t)mp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
prstop(PR_REQUESTED, 0);
timedwait = (caddr_t)tsp;
if ((error = lwp_timer_copyin(&lwpt, tsp)) != 0)
return (set_errno(error));
if (lwpt.lwpt_imm_timeout) {
imm_timeout = 1;
timedwait = NULL;
}
(void) new_mstate(t, LMS_USER_LOCK);
if (on_fault(&ljb)) {
if (no_lwpchan) {
error = EFAULT;
goto out;
}
if (m_locked) {
m_locked = 0;
lwpchan_unlock(&m_lwpchan, LWPCHAN_MPPOOL);
}
if (locked) {
locked = 0;
lwpchan_unlock(&cv_lwpchan, LWPCHAN_CVPOOL);
}
if (on_fault(&ljb)) {
if (m_locked) {
m_locked = 0;
lwpchan_unlock(&m_lwpchan, LWPCHAN_MPPOOL);
}
goto out;
}
error = EFAULT;
goto efault;
}
fuword8_noerr(&mp->mutex_type, (uint8_t *)&mtype);
suword8_noerr(&mp->mutex_type, mtype);
if (UPIMUTEX(mtype) == 0) {
if (!get_lwpchan(p->p_as, (caddr_t)mp, mtype,
&m_lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
}
fuword16_noerr(&cv->cond_type, (uint16_t *)&type);
suword16_noerr(&cv->cond_type, type);
if (!get_lwpchan(p->p_as, (caddr_t)cv, type,
&cv_lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
no_lwpchan = 0;
cvwatched = watch_disable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (UPIMUTEX(mtype) == 0)
mpwatched = watch_disable_addr((caddr_t)mp, sizeof (*mp),
S_WRITE);
lwpchan_lock(&cv_lwpchan, LWPCHAN_CVPOOL);
locked = 1;
if (UPIMUTEX(mtype) == 0) {
lwpchan_lock(&m_lwpchan, LWPCHAN_MPPOOL);
m_locked = 1;
suword8_noerr(&cv->cond_waiters_kernel, 1);
set_owner_pid(mp, 0, 0);
ulock_clear(&mp->mutex_lockw);
fuword8_noerr(&mp->mutex_waiters, &waiters);
if (waiters != 0) {
if (lwp_release(&m_lwpchan, &waiters, 0))
suword8_noerr(&mp->mutex_waiters, waiters);
}
m_locked = 0;
lwpchan_unlock(&m_lwpchan, LWPCHAN_MPPOOL);
} else {
suword8_noerr(&cv->cond_waiters_kernel, 1);
error = lwp_upimutex_unlock(mp, mtype);
if (error) {
locked = 0;
lwpchan_unlock(&cv_lwpchan, LWPCHAN_CVPOOL);
goto out;
}
}
no_fault();
if (mpwatched) {
watch_enable_addr((caddr_t)mp, sizeof (*mp), S_WRITE);
mpwatched = 0;
}
if (cvwatched) {
watch_enable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
cvwatched = 0;
}
if (check_park && (!schedctl_is_park() || t->t_unpark)) {
imm_unpark = 1;
t->t_unpark = 0;
timedwait = NULL;
} else if (timedwait) {
mutex_enter(&t->t_delay_lock);
if (lwp_timer_enqueue(&lwpt) != 0) {
mutex_exit(&t->t_delay_lock);
imm_timeout = 1;
timedwait = NULL;
}
}
t->t_flag |= T_WAITCVSEM;
lwp_block(&cv_lwpchan);
if (timedwait)
mutex_exit(&t->t_delay_lock);
locked = 0;
lwpchan_unlock(&cv_lwpchan, LWPCHAN_CVPOOL);
if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) ||
(imm_timeout | imm_unpark))
setrun(t);
swtch();
t->t_flag &= ~(T_WAITCVSEM | T_WAKEABLE);
if (timedwait)
tim = lwp_timer_dequeue(&lwpt);
if (ISSIG(t, FORREAL) || lwp->lwp_sysabort ||
MUSTRETURN(p, t) || imm_unpark)
error = EINTR;
else if (imm_timeout || (timedwait && tim == -1))
error = ETIME;
lwp->lwp_asleep = 0;
lwp->lwp_sysabort = 0;
setallwatch();
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
if (tsp && check_park)
error = lwp_timer_copyout(&lwpt, error);
if (error) {
if (t->t_release)
(void) lwp_cond_signal(cv);
return (set_errno(error));
}
return (0);
efault:
if (UPIMUTEX(mtype) == 0) {
lwpchan_lock(&m_lwpchan, LWPCHAN_MPPOOL);
m_locked = 1;
set_owner_pid(mp, 0, 0);
ulock_clear(&mp->mutex_lockw);
fuword8_noerr(&mp->mutex_waiters, &waiters);
if (waiters != 0) {
if (lwp_release(&m_lwpchan, &waiters, 0))
suword8_noerr(&mp->mutex_waiters, waiters);
}
m_locked = 0;
lwpchan_unlock(&m_lwpchan, LWPCHAN_MPPOOL);
} else {
(void) lwp_upimutex_unlock(mp, mtype);
}
out:
no_fault();
if (mpwatched)
watch_enable_addr((caddr_t)mp, sizeof (*mp), S_WRITE);
if (cvwatched)
watch_enable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
return (set_errno(error));
}
int
lwp_cond_signal(lwp_cond_t *cv)
{
proc_t *p = ttoproc(curthread);
lwpchan_t lwpchan;
uchar_t waiters;
volatile uint16_t type = 0;
volatile int locked = 0;
volatile int watched = 0;
label_t ljb;
int error = 0;
if ((caddr_t)cv >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
error = EFAULT;
goto out;
}
fuword16_noerr(&cv->cond_type, (uint16_t *)&type);
suword16_noerr(&cv->cond_type, type);
if (!get_lwpchan(curproc->p_as, (caddr_t)cv, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword8_noerr(&cv->cond_waiters_kernel, &waiters);
if (waiters != 0) {
(void) lwp_release(&lwpchan, &waiters, T_WAITCVSEM);
suword8_noerr(&cv->cond_waiters_kernel, waiters);
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_cond_broadcast(lwp_cond_t *cv)
{
proc_t *p = ttoproc(curthread);
lwpchan_t lwpchan;
volatile uint16_t type = 0;
volatile int locked = 0;
volatile int watched = 0;
label_t ljb;
uchar_t waiters;
int error = 0;
if ((caddr_t)cv >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
error = EFAULT;
goto out;
}
fuword16_noerr(&cv->cond_type, (uint16_t *)&type);
suword16_noerr(&cv->cond_type, type);
if (!get_lwpchan(curproc->p_as, (caddr_t)cv, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword8_noerr(&cv->cond_waiters_kernel, &waiters);
if (waiters != 0) {
lwp_release_all(&lwpchan);
suword8_noerr(&cv->cond_waiters_kernel, 0);
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)cv, sizeof (*cv), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_sema_trywait(lwp_sema_t *sp)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
label_t ljb;
volatile int locked = 0;
volatile int watched = 0;
volatile uint16_t type = 0;
int count;
lwpchan_t lwpchan;
uchar_t waiters;
int error = 0;
if ((caddr_t)sp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
error = EFAULT;
goto out;
}
fuword16_noerr((void *)&sp->sema_type, (uint16_t *)&type);
suword16_noerr((void *)&sp->sema_type, type);
if (!get_lwpchan(p->p_as, (caddr_t)sp, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword32_noerr((void *)&sp->sema_count, (uint32_t *)&count);
if (count == 0)
error = EBUSY;
else
suword32_noerr((void *)&sp->sema_count, --count);
if (count != 0) {
fuword8_noerr(&sp->sema_waiters, &waiters);
if (waiters != 0) {
(void) lwp_release(&lwpchan, &waiters, T_WAITCVSEM);
suword8_noerr(&sp->sema_waiters, waiters);
}
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_sema_timedwait(lwp_sema_t *sp, timespec_t *tsp, int check_park)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
proc_t *p = ttoproc(t);
lwp_timer_t lwpt;
caddr_t timedwait;
clock_t tim = -1;
label_t ljb;
volatile int locked = 0;
volatile int watched = 0;
volatile uint16_t type = 0;
int count;
lwpchan_t lwpchan;
uchar_t waiters;
int error = 0;
int time_error;
int imm_timeout = 0;
int imm_unpark = 0;
if ((caddr_t)sp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
prstop(PR_REQUESTED, 0);
timedwait = (caddr_t)tsp;
if ((time_error = lwp_timer_copyin(&lwpt, tsp)) == 0 &&
lwpt.lwpt_imm_timeout) {
imm_timeout = 1;
timedwait = NULL;
}
watched = watch_disable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
error = EFAULT;
goto out;
}
fuword16_noerr((void *)&sp->sema_type, (uint16_t *)&type);
suword16_noerr((void *)&sp->sema_type, type);
if (!get_lwpchan(p->p_as, (caddr_t)sp, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword32_noerr((void *)&sp->sema_count, (uint32_t *)&count);
while (error == 0 && count == 0) {
if (time_error) {
error = time_error;
break;
}
suword8_noerr(&sp->sema_waiters, 1);
if (watched)
watch_enable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (check_park && (!schedctl_is_park() || t->t_unpark)) {
imm_unpark = 1;
t->t_unpark = 0;
timedwait = NULL;
} else if (timedwait) {
mutex_enter(&t->t_delay_lock);
if (lwp_timer_enqueue(&lwpt) != 0) {
mutex_exit(&t->t_delay_lock);
imm_timeout = 1;
timedwait = NULL;
}
}
t->t_flag |= T_WAITCVSEM;
lwp_block(&lwpchan);
if (timedwait)
mutex_exit(&t->t_delay_lock);
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) ||
(imm_timeout | imm_unpark))
setrun(t);
swtch();
t->t_flag &= ~(T_WAITCVSEM | T_WAKEABLE);
if (timedwait)
tim = lwp_timer_dequeue(&lwpt);
setallwatch();
if (ISSIG(t, FORREAL) || lwp->lwp_sysabort ||
MUSTRETURN(p, t) || imm_unpark)
error = EINTR;
else if (imm_timeout || (timedwait && tim == -1))
error = ETIME;
lwp->lwp_asleep = 0;
lwp->lwp_sysabort = 0;
watched = watch_disable_addr((caddr_t)sp,
sizeof (*sp), S_WRITE);
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword32_noerr((void *)&sp->sema_count, (uint32_t *)&count);
}
if (error == 0)
suword32_noerr((void *)&sp->sema_count, --count);
if (count != 0) {
(void) lwp_release(&lwpchan, &waiters, T_WAITCVSEM);
suword8_noerr(&sp->sema_waiters, waiters);
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (tsp && check_park && !time_error)
error = lwp_timer_copyout(&lwpt, error);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_sema_post(lwp_sema_t *sp)
{
proc_t *p = ttoproc(curthread);
label_t ljb;
volatile int locked = 0;
volatile int watched = 0;
volatile uint16_t type = 0;
int count;
lwpchan_t lwpchan;
uchar_t waiters;
int error = 0;
if ((caddr_t)sp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
error = EFAULT;
goto out;
}
fuword16_noerr(&sp->sema_type, (uint16_t *)&type);
suword16_noerr(&sp->sema_type, type);
if (!get_lwpchan(curproc->p_as, (caddr_t)sp, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword32_noerr(&sp->sema_count, (uint32_t *)&count);
if (count == _SEM_VALUE_MAX)
error = EOVERFLOW;
else
suword32_noerr(&sp->sema_count, ++count);
if (count == 1) {
fuword8_noerr(&sp->sema_waiters, &waiters);
if (waiters) {
(void) lwp_release(&lwpchan, &waiters, T_WAITCVSEM);
suword8_noerr(&sp->sema_waiters, waiters);
}
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)sp, sizeof (*sp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
#define TRW_WANT_WRITE 0x1
#define TRW_LOCK_GRANTED 0x2
#define READ_LOCK 0
#define WRITE_LOCK 1
#define TRY_FLAG 0x10
#define READ_LOCK_TRY (READ_LOCK | TRY_FLAG)
#define WRITE_LOCK_TRY (WRITE_LOCK | TRY_FLAG)
void
lwp_rwlock_release(lwpchan_t *lwpchan, lwp_rwlock_t *rw)
{
sleepq_head_t *sqh;
kthread_t *tp;
kthread_t **tpp;
kthread_t *tpnext;
kthread_t *wakelist = NULL;
uint32_t rwstate = 0;
int wcount = 0;
int rcount = 0;
sqh = lwpsqhash(lwpchan);
disp_lock_enter(&sqh->sq_lock);
tpp = &sqh->sq_queue.sq_first;
while ((tp = *tpp) != NULL) {
if (tp->t_lwpchan.lc_wchan0 == lwpchan->lc_wchan0 &&
tp->t_lwpchan.lc_wchan == lwpchan->lc_wchan) {
if (tp->t_writer & TRW_WANT_WRITE) {
if ((wcount++ == 0) && (rcount == 0)) {
rwstate |= URW_WRITE_LOCKED;
sleepq_unlink(tpp, tp);
wakelist = tp;
continue;
} else {
rwstate |= URW_HAS_WAITERS;
break;
}
} else {
rcount++;
if (wcount == 0) {
rwstate++;
sleepq_unlink(tpp, tp);
tp->t_link = wakelist;
wakelist = tp;
continue;
} else {
rwstate |= URW_HAS_WAITERS;
break;
}
}
}
tpp = &tp->t_link;
}
suword32_noerr(&rw->rwlock_readers, rwstate);
tp = wakelist;
while (tp != NULL) {
DTRACE_SCHED1(wakeup, kthread_t *, tp);
tp->t_wchan0 = NULL;
tp->t_wchan = NULL;
tp->t_sobj_ops = NULL;
tp->t_writer |= TRW_LOCK_GRANTED;
tpnext = tp->t_link;
tp->t_link = NULL;
CL_WAKEUP(tp);
thread_unlock_high(tp);
tp = tpnext;
}
disp_lock_exit(&sqh->sq_lock);
}
static int
lwp_rwlock_lock(lwp_rwlock_t *rw, timespec_t *tsp, int rd_wr)
{
lwp_mutex_t *mp = NULL;
kthread_t *t = curthread;
kthread_t *tp;
klwp_t *lwp = ttolwp(t);
proc_t *p = ttoproc(t);
lwp_timer_t lwpt;
lwpchan_t lwpchan;
lwpchan_t mlwpchan;
caddr_t timedwait;
volatile uint16_t type = 0;
volatile uint8_t mtype = 0;
uchar_t mwaiters;
volatile int error = 0;
int time_error;
clock_t tim = -1;
volatile int locked = 0;
volatile int mlocked = 0;
volatile int watched = 0;
volatile int mwatched = 0;
label_t ljb;
volatile int no_lwpchan = 1;
int imm_timeout = 0;
int try_flag;
uint32_t rwstate;
int acquired = 0;
if ((caddr_t)rw >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
prstop(PR_REQUESTED, 0);
timedwait = (caddr_t)tsp;
if ((time_error = lwp_timer_copyin(&lwpt, tsp)) == 0 &&
lwpt.lwpt_imm_timeout) {
imm_timeout = 1;
timedwait = NULL;
}
(void) new_mstate(t, LMS_USER_LOCK);
if (on_fault(&ljb)) {
if (no_lwpchan) {
error = EFAULT;
goto out_nodrop;
}
if (mlocked) {
mlocked = 0;
lwpchan_unlock(&mlwpchan, LWPCHAN_MPPOOL);
}
if (locked) {
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
}
if (on_fault(&ljb)) {
if (mlocked) {
mlocked = 0;
lwpchan_unlock(&mlwpchan, LWPCHAN_MPPOOL);
}
error = EFAULT;
goto out_nodrop;
}
error = EFAULT;
goto out_nodrop;
}
try_flag = (rd_wr & TRY_FLAG);
rd_wr &= ~TRY_FLAG;
if ((rd_wr != READ_LOCK) && (rd_wr != WRITE_LOCK)) {
error = EINVAL;
goto out_nodrop;
}
mp = &rw->mutex;
fuword8_noerr(&mp->mutex_type, (uint8_t *)&mtype);
fuword16_noerr(&rw->rwlock_type, (uint16_t *)&type);
suword8_noerr(&mp->mutex_type, mtype);
suword16_noerr(&rw->rwlock_type, type);
if ((mtype != USYNC_PROCESS) || (type != USYNC_PROCESS)) {
error = EINVAL;
goto out_nodrop;
}
if (!get_lwpchan(p->p_as, (caddr_t)mp, mtype,
&mlwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out_nodrop;
}
if (!get_lwpchan(p->p_as, (caddr_t)rw, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out_nodrop;
}
no_lwpchan = 0;
watched = watch_disable_addr((caddr_t)rw, sizeof (*rw), S_WRITE);
mwatched = watch_disable_addr((caddr_t)mp, sizeof (*mp), S_WRITE);
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
lwpchan_lock(&mlwpchan, LWPCHAN_MPPOOL);
mlocked = 1;
fuword32_noerr(&rw->rwlock_readers, &rwstate);
rwstate |= URW_HAS_WAITERS;
if (!(rwstate & URW_WRITE_LOCKED)) {
tp = lwp_queue_waiter(&lwpchan);
if (tp == NULL) {
if (rd_wr == READ_LOCK) {
rwstate++;
acquired = 1;
} else if ((rwstate & URW_READERS_MASK) == 0) {
rwstate |= URW_WRITE_LOCKED;
acquired = 1;
}
} else if (rd_wr == READ_LOCK) {
pri_t our_pri = DISP_PRIO(t);
pri_t his_pri = DISP_PRIO(tp);
if ((our_pri > his_pri) || ((our_pri == his_pri) &&
!(tp->t_writer & TRW_WANT_WRITE))) {
rwstate++;
acquired = 1;
}
}
}
if (acquired || try_flag || time_error) {
suword32_noerr(&rw->rwlock_readers, rwstate);
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
locked = 0;
if (acquired) {
error = 0;
} else if (try_flag) {
error = EBUSY;
} else if (time_error) {
error = time_error;
}
goto out_drop;
}
t->t_writer = 0;
if (rd_wr == WRITE_LOCK)
t->t_writer = TRW_WANT_WRITE;
suword32_noerr(&rw->rwlock_readers, rwstate);
set_owner_pid(mp, 0, 0);
ulock_clear(&mp->mutex_lockw);
fuword8_noerr(&mp->mutex_waiters, &mwaiters);
if (mwaiters != 0) {
if (lwp_release(&mlwpchan, &mwaiters, 0))
suword8_noerr(&mp->mutex_waiters, mwaiters);
}
lwpchan_unlock(&mlwpchan, LWPCHAN_MPPOOL);
mlocked = 0;
no_fault();
if (mwatched) {
watch_enable_addr((caddr_t)mp, sizeof (*mp), S_WRITE);
mwatched = 0;
}
if (watched) {
watch_enable_addr((caddr_t)rw, sizeof (*rw), S_WRITE);
watched = 0;
}
if (timedwait) {
mutex_enter(&t->t_delay_lock);
if (lwp_timer_enqueue(&lwpt) != 0) {
mutex_exit(&t->t_delay_lock);
imm_timeout = 1;
timedwait = NULL;
}
}
t->t_flag |= T_WAITCVSEM;
lwp_block(&lwpchan);
if (timedwait)
mutex_exit(&t->t_delay_lock);
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
if (ISSIG(t, JUSTLOOKING) || MUSTRETURN(p, t) || imm_timeout)
setrun(t);
swtch();
error = EAGAIN;
acquired = (t->t_writer & TRW_LOCK_GRANTED);
t->t_writer = 0;
t->t_flag &= ~(T_WAITCVSEM | T_WAKEABLE);
if (timedwait)
tim = lwp_timer_dequeue(&lwpt);
if (ISSIG(t, FORREAL) || lwp->lwp_sysabort || MUSTRETURN(p, t))
error = EINTR;
else if (imm_timeout || (timedwait && tim == -1))
error = ETIME;
lwp->lwp_asleep = 0;
lwp->lwp_sysabort = 0;
setallwatch();
if (acquired)
error = 0;
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
if (error)
return (set_errno(error));
return (0);
out_drop:
if (!mlocked) {
lwpchan_lock(&mlwpchan, LWPCHAN_MPPOOL);
mlocked = 1;
}
set_owner_pid(mp, 0, 0);
ulock_clear(&mp->mutex_lockw);
fuword8_noerr(&mp->mutex_waiters, &mwaiters);
if (mwaiters != 0) {
if (lwp_release(&mlwpchan, &mwaiters, 0))
suword8_noerr(&mp->mutex_waiters, mwaiters);
}
lwpchan_unlock(&mlwpchan, LWPCHAN_MPPOOL);
mlocked = 0;
out_nodrop:
no_fault();
if (mwatched)
watch_enable_addr((caddr_t)mp, sizeof (*mp), S_WRITE);
if (watched)
watch_enable_addr((caddr_t)rw, sizeof (*rw), S_WRITE);
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
if (error)
return (set_errno(error));
return (0);
}
static int
lwp_rwlock_unlock(lwp_rwlock_t *rw)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
lwpchan_t lwpchan;
volatile uint16_t type = 0;
volatile int error = 0;
volatile int locked = 0;
volatile int watched = 0;
label_t ljb;
volatile int no_lwpchan = 1;
uint32_t rwstate;
if ((caddr_t)rw >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
if (on_fault(&ljb)) {
if (no_lwpchan) {
error = EFAULT;
goto out_nodrop;
}
if (locked) {
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
}
error = EFAULT;
goto out_nodrop;
}
fuword16_noerr(&rw->rwlock_type, (uint16_t *)&type);
suword16_noerr(&rw->rwlock_type, type);
if (type != USYNC_PROCESS) {
error = EINVAL;
goto out_nodrop;
}
if (!get_lwpchan(p->p_as, (caddr_t)rw, type,
&lwpchan, LWPCHAN_CVPOOL)) {
error = EFAULT;
goto out_nodrop;
}
no_lwpchan = 0;
watched = watch_disable_addr((caddr_t)rw, sizeof (*rw), S_WRITE);
lwpchan_lock(&lwpchan, LWPCHAN_CVPOOL);
locked = 1;
fuword32_noerr(&rw->rwlock_readers, &rwstate);
if (rwstate & URW_WRITE_LOCKED)
lwp_rwlock_release(&lwpchan, rw);
else if ((rwstate & URW_READERS_MASK) > 0) {
rwstate--;
if ((rwstate & URW_READERS_MASK) == 0)
lwp_rwlock_release(&lwpchan, rw);
else
suword32_noerr(&rw->rwlock_readers, rwstate);
}
lwpchan_unlock(&lwpchan, LWPCHAN_CVPOOL);
locked = 0;
error = 0;
out_nodrop:
no_fault();
if (watched)
watch_enable_addr((caddr_t)rw, sizeof (*rw), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_rwlock_sys(int subcode, lwp_rwlock_t *rwlp, timespec_t *tsp)
{
switch (subcode) {
case 0:
return (lwp_rwlock_lock(rwlp, tsp, READ_LOCK));
case 1:
return (lwp_rwlock_lock(rwlp, tsp, WRITE_LOCK));
case 2:
return (lwp_rwlock_lock(rwlp, NULL, READ_LOCK_TRY));
case 3:
return (lwp_rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY));
case 4:
return (lwp_rwlock_unlock(rwlp));
}
return (set_errno(EINVAL));
}
static kthread_t *
lwpsobj_owner(caddr_t sobj)
{
return ((kthread_t *)NULL);
}
static void
lwp_unsleep(kthread_t *t)
{
ASSERT(THREAD_LOCK_HELD(t));
if (t->t_wchan0 != NULL) {
sleepq_head_t *sqh;
sleepq_t *sqp = t->t_sleepq;
if (sqp != NULL) {
sqh = lwpsqhash(&t->t_lwpchan);
ASSERT(&sqh->sq_queue == sqp);
sleepq_unsleep(t);
disp_lock_exit_high(&sqh->sq_lock);
CL_SETRUN(t);
return;
}
}
panic("lwp_unsleep: thread %p not on sleepq", (void *)t);
}
static void
lwp_change_pri(kthread_t *t, pri_t pri, pri_t *t_prip)
{
ASSERT(THREAD_LOCK_HELD(t));
if (t->t_wchan0 != NULL) {
sleepq_t *sqp = t->t_sleepq;
sleepq_dequeue(t);
*t_prip = pri;
sleepq_insert(sqp, t);
} else
panic("lwp_change_pri: %p not on a sleep queue", (void *)t);
}
static void
lwp_mutex_cleanup(lwpchan_entry_t *ent, uint16_t lockflg)
{
uint16_t flag;
uchar_t waiters;
label_t ljb;
pid_t owner_pid;
lwp_mutex_t *lp;
volatile int locked = 0;
volatile int watched = 0;
volatile struct upimutex *upimutex = NULL;
volatile int upilocked = 0;
if ((ent->lwpchan_type & (USYNC_PROCESS | LOCK_ROBUST))
!= (USYNC_PROCESS | LOCK_ROBUST))
return;
lp = (lwp_mutex_t *)ent->lwpchan_addr;
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&ent->lwpchan_lwpchan, LWPCHAN_MPPOOL);
if (upilocked)
upimutex_unlock((upimutex_t *)upimutex, 0);
goto out;
}
fuword32_noerr(&lp->mutex_ownerpid, (uint32_t *)&owner_pid);
if (UPIMUTEX(ent->lwpchan_type)) {
lwpchan_t lwpchan = ent->lwpchan_lwpchan;
upib_t *upibp = &UPI_CHAIN(lwpchan);
if (owner_pid != curproc->p_pid)
goto out;
mutex_enter(&upibp->upib_lock);
upimutex = upi_get(upibp, &lwpchan);
if (upimutex == NULL || upimutex->upi_owner != curthread) {
mutex_exit(&upibp->upib_lock);
goto out;
}
mutex_exit(&upibp->upib_lock);
upilocked = 1;
flag = lwp_clear_mutex(lp, lockflg);
suword8_noerr(&lp->mutex_lockw, 0);
upimutex_unlock((upimutex_t *)upimutex, flag);
} else {
lwpchan_lock(&ent->lwpchan_lwpchan, LWPCHAN_MPPOOL);
locked = 1;
suword8_noerr(&lp->mutex_spinners, 0);
if (owner_pid != curproc->p_pid) {
fuword8_noerr(&lp->mutex_waiters, &waiters);
if (waiters != 0) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & LOCK_NOTRECOVERABLE) {
lwp_release_all(&ent->lwpchan_lwpchan);
suword8_noerr(&lp->mutex_waiters, 0);
} else if (lwp_release(&ent->lwpchan_lwpchan,
&waiters, 0)) {
suword8_noerr(&lp->mutex_waiters,
waiters);
}
}
} else {
(void) lwp_clear_mutex(lp, lockflg);
ulock_clear(&lp->mutex_lockw);
fuword8_noerr(&lp->mutex_waiters, &waiters);
if (waiters &&
lwp_release(&ent->lwpchan_lwpchan, &waiters, 0))
suword8_noerr(&lp->mutex_waiters, waiters);
}
lwpchan_unlock(&ent->lwpchan_lwpchan, LWPCHAN_MPPOOL);
}
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
}
int
lwp_mutex_register(lwp_mutex_t *lp, caddr_t uaddr)
{
int error = 0;
volatile int watched;
label_t ljb;
uint8_t type;
lwpchan_t lwpchan;
if ((caddr_t)lp >= (caddr_t)USERLIMIT)
return (set_errno(EFAULT));
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (on_fault(&ljb)) {
error = EFAULT;
} else {
fuword8_noerr(&lp->mutex_type, &type);
suword8_noerr(&lp->mutex_type, type);
if ((type & (USYNC_PROCESS|LOCK_ROBUST))
!= (USYNC_PROCESS|LOCK_ROBUST)) {
error = EINVAL;
} else if (!lwpchan_get_mapping(curproc->p_as, (caddr_t)lp,
uaddr, type, &lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
}
}
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
static void
lwp_mutex_unregister(void *uaddr)
{
if (get_udatamodel() == DATAMODEL_NATIVE) {
(void) sulword(uaddr, (ulong_t)-1);
#ifdef _SYSCALL32_IMPL
} else {
(void) suword32(uaddr, (uint32_t)-1);
#endif
}
}
int
lwp_mutex_trylock(lwp_mutex_t *lp, uintptr_t owner)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
int error = 0;
volatile int locked = 0;
volatile int watched = 0;
label_t ljb;
volatile uint8_t type = 0;
uint16_t flag;
lwpchan_t lwpchan;
if ((caddr_t)lp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
(void) new_mstate(t, LMS_USER_LOCK);
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = EFAULT;
goto out;
}
fuword8_noerr(&lp->mutex_type, (uint8_t *)&type);
suword8_noerr(&lp->mutex_type, type);
if (UPIMUTEX(type)) {
no_fault();
error = lwp_upimutex_lock(lp, type, UPIMUTEX_TRY, NULL);
if (error == 0 || error == EOWNERDEAD ||
error == ELOCKUNMAPPED) {
volatile int locked = error != 0;
if (on_fault(&ljb)) {
if (locked != 0)
error = lwp_upimutex_unlock(lp, type);
else
error = EFAULT;
goto upierr;
}
set_owner_pid(lp, owner,
(type & USYNC_PROCESS)? p->p_pid : 0);
no_fault();
}
upierr:
if (error)
return (set_errno(error));
return (0);
}
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & LOCK_NOTRECOVERABLE) {
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = ENOTRECOVERABLE;
goto out;
}
}
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (!ulock_try(&lp->mutex_lockw))
error = EBUSY;
else {
set_owner_pid(lp, owner, (type & USYNC_PROCESS)? p->p_pid : 0);
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
if (flag & LOCK_OWNERDEAD)
error = EOWNERDEAD;
else if (type & USYNC_PROCESS_ROBUST)
error = ELOCKUNMAPPED;
else
error = EOWNERDEAD;
}
}
}
locked = 0;
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
out:
if (t->t_mstate == LMS_USER_LOCK)
(void) new_mstate(t, LMS_SYSTEM);
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}
int
lwp_mutex_unlock(lwp_mutex_t *lp)
{
proc_t *p = ttoproc(curthread);
lwpchan_t lwpchan;
uchar_t waiters;
volatile int locked = 0;
volatile int watched = 0;
volatile uint8_t type = 0;
label_t ljb;
uint16_t flag;
int error = 0;
if ((caddr_t)lp >= p->p_as->a_userlimit)
return (set_errno(EFAULT));
if (on_fault(&ljb)) {
if (locked)
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
error = EFAULT;
goto out;
}
fuword8_noerr(&lp->mutex_type, (uint8_t *)&type);
suword8_noerr(&lp->mutex_type, type);
if (UPIMUTEX(type)) {
no_fault();
error = lwp_upimutex_unlock(lp, type);
if (error)
return (set_errno(error));
return (0);
}
watched = watch_disable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (!get_lwpchan(curproc->p_as, (caddr_t)lp, type,
&lwpchan, LWPCHAN_MPPOOL)) {
error = EFAULT;
goto out;
}
lwpchan_lock(&lwpchan, LWPCHAN_MPPOOL);
locked = 1;
if (type & LOCK_ROBUST) {
fuword16_noerr(&lp->mutex_flag, &flag);
if (flag & (LOCK_OWNERDEAD | LOCK_UNMAPPED)) {
flag &= ~(LOCK_OWNERDEAD | LOCK_UNMAPPED);
flag |= LOCK_NOTRECOVERABLE;
suword16_noerr(&lp->mutex_flag, flag);
}
}
set_owner_pid(lp, 0, 0);
ulock_clear(&lp->mutex_lockw);
fuword8_noerr(&lp->mutex_waiters, &waiters);
if (waiters) {
if ((type & LOCK_ROBUST) &&
(flag & LOCK_NOTRECOVERABLE)) {
lwp_release_all(&lwpchan);
suword8_noerr(&lp->mutex_waiters, 0);
} else if (lwp_release(&lwpchan, &waiters, 0)) {
suword8_noerr(&lp->mutex_waiters, waiters);
}
}
lwpchan_unlock(&lwpchan, LWPCHAN_MPPOOL);
out:
no_fault();
if (watched)
watch_enable_addr((caddr_t)lp, sizeof (*lp), S_WRITE);
if (error)
return (set_errno(error));
return (0);
}