#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/thread.h>
#include <sys/proc.h>
#include <sys/task.h>
#include <sys/project.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/vmparam.h>
#include <sys/stack.h>
#include <sys/procfs.h>
#include <sys/prsystm.h>
#include <sys/cpuvar.h>
#include <sys/kmem.h>
#include <sys/vtrace.h>
#include <sys/door.h>
#include <vm/seg_kp.h>
#include <sys/debug.h>
#include <sys/schedctl.h>
#include <sys/poll.h>
#include <sys/copyops.h>
#include <sys/lwp_upimutex_impl.h>
#include <sys/cpupart.h>
#include <sys/lgrp.h>
#include <sys/rctl.h>
#include <sys/contract_impl.h>
#include <sys/cpc_impl.h>
#include <sys/sdt.h>
#include <sys/cmn_err.h>
#include <sys/brand.h>
#include <sys/cyclic.h>
#include <sys/pool.h>
#define TIDHASH(tid, hash_sz) ((tid) & ((hash_sz) - 1))
void *segkp_lwp;
extern void reapq_move_lq_to_tq(kthread_t *);
extern void freectx_ctx(struct ctxop *);
kthread_t *
lwp_kernel_create(proc_t *p, void (*proc)(), void *arg, int state, pri_t pri)
{
klwp_t *lwp;
VERIFY((p->p_flag & SSYS) != 0);
lwp = lwp_create(proc, arg, 0, p, state, pri, &t0.t_hold, syscid, 0);
VERIFY(lwp != NULL);
return (lwptot(lwp));
}
klwp_t *
lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
int state, int pri, const k_sigset_t *smask, int cid, id_t lwpid)
{
klwp_t *lwp = NULL;
kthread_t *t;
kthread_t *tx;
cpupart_t *oldpart = NULL;
size_t stksize;
caddr_t lwpdata = NULL;
processorid_t binding;
int err = 0;
kproject_t *oldkpj, *newkpj;
void *bufp = NULL;
klwp_t *curlwp;
lwpent_t *lep;
lwpdir_t *old_dir = NULL;
uint_t old_dirsz = 0;
tidhash_t *old_hash = NULL;
uint_t old_hashsz = 0;
ret_tidhash_t *ret_tidhash = NULL;
int i;
int rctlfail = 0;
boolean_t branded = 0;
struct ctxop *ctx = NULL;
ASSERT(cid != sysdccid);
ASSERT(p != &p0);
mutex_enter(&p->p_lock);
mutex_enter(&p->p_zone->zone_nlwps_lock);
if (!CLASS_KERNEL(cid)) {
if (p->p_task->tk_nlwps >= p->p_task->tk_nlwps_ctl)
if (rctl_test(rc_task_lwps, p->p_task->tk_rctls, p,
1, 0) & RCT_DENY)
rctlfail = 1;
if (p->p_task->tk_proj->kpj_nlwps >=
p->p_task->tk_proj->kpj_nlwps_ctl)
if (rctl_test(rc_project_nlwps,
p->p_task->tk_proj->kpj_rctls, p, 1, 0)
& RCT_DENY)
rctlfail = 1;
if (p->p_zone->zone_nlwps >= p->p_zone->zone_nlwps_ctl)
if (rctl_test(rc_zone_nlwps, p->p_zone->zone_rctls, p,
1, 0) & RCT_DENY)
rctlfail = 1;
}
if (rctlfail) {
mutex_exit(&p->p_zone->zone_nlwps_lock);
mutex_exit(&p->p_lock);
atomic_inc_32(&p->p_zone->zone_ffcap);
return (NULL);
}
p->p_task->tk_nlwps++;
p->p_task->tk_proj->kpj_nlwps++;
p->p_zone->zone_nlwps++;
mutex_exit(&p->p_zone->zone_nlwps_lock);
mutex_exit(&p->p_lock);
curlwp = ttolwp(curthread);
if (curlwp == NULL || (stksize = curlwp->lwp_childstksz) == 0)
stksize = lwp_default_stksize;
if (CLASS_KERNEL(cid)) {
curlwp = NULL;
stksize = 0;
lwpdata = NULL;
} else if (stksize == lwp_default_stksize) {
if (lwp_reapcnt > 0) {
mutex_enter(&reaplock);
if ((t = lwp_deathrow) != NULL) {
ASSERT(t->t_swap);
lwp_deathrow = t->t_forw;
lwp_reapcnt--;
lwpdata = t->t_swap;
lwp = t->t_lwp;
ctx = t->t_ctx;
t->t_swap = NULL;
t->t_lwp = NULL;
t->t_ctx = NULL;
reapq_move_lq_to_tq(t);
}
mutex_exit(&reaplock);
if (lwp != NULL) {
lwp_stk_fini(lwp);
}
if (ctx != NULL) {
freectx_ctx(ctx);
}
}
if (lwpdata == NULL &&
(lwpdata = (caddr_t)segkp_cache_get(segkp_lwp)) == NULL) {
mutex_enter(&p->p_lock);
mutex_enter(&p->p_zone->zone_nlwps_lock);
p->p_task->tk_nlwps--;
p->p_task->tk_proj->kpj_nlwps--;
p->p_zone->zone_nlwps--;
mutex_exit(&p->p_zone->zone_nlwps_lock);
mutex_exit(&p->p_lock);
atomic_inc_32(&p->p_zone->zone_ffnomem);
return (NULL);
}
} else {
stksize = roundup(stksize, PAGESIZE);
if ((lwpdata = (caddr_t)segkp_get(segkp, stksize,
(KPD_NOWAIT | KPD_HASREDZONE | KPD_LOCKED))) == NULL) {
mutex_enter(&p->p_lock);
mutex_enter(&p->p_zone->zone_nlwps_lock);
p->p_task->tk_nlwps--;
p->p_task->tk_proj->kpj_nlwps--;
p->p_zone->zone_nlwps--;
mutex_exit(&p->p_zone->zone_nlwps_lock);
mutex_exit(&p->p_lock);
atomic_inc_32(&p->p_zone->zone_ffnomem);
return (NULL);
}
}
t = thread_create(lwpdata, stksize, NULL, NULL, 0, p, TS_STOPPED, pri);
if (lwpdata != NULL) {
ASSERT(!CLASS_KERNEL(cid));
ASSERT(t->t_swap == NULL);
t->t_swap = lwpdata;
}
if (!CLASS_KERNEL(cid) && stksize == lwp_default_stksize)
t->t_flag |= T_LWPREUSE;
if (lwp == NULL)
lwp = kmem_cache_alloc(lwp_cache, KM_SLEEP);
bzero(lwp, sizeof (*lwp));
t->t_lwp = lwp;
t->t_hold = *smask;
lwp->lwp_thread = t;
lwp->lwp_procp = p;
lwp->lwp_sigaltstack.ss_flags = SS_DISABLE;
if (curlwp != NULL && curlwp->lwp_childstksz != 0)
lwp->lwp_childstksz = curlwp->lwp_childstksz;
t->t_stk = lwp_stk_init(lwp, t->t_stk);
thread_load(t, proc, arg, len);
if (p->p_rprof_cyclic != CYCLIC_NONE)
t->t_rprof = kmem_zalloc(sizeof (struct rprof), KM_SLEEP);
if (cid != NOCLASS)
(void) CL_ALLOC(&bufp, cid, KM_SLEEP);
lep = kmem_zalloc(sizeof (*lep), KM_SLEEP);
mutex_enter(&p->p_lock);
grow:
while (p->p_lwpfree == NULL) {
uint_t dirsz = p->p_lwpdir_sz;
lwpdir_t *new_dir;
uint_t new_dirsz;
lwpdir_t *ldp;
tidhash_t *new_hash;
uint_t new_hashsz;
mutex_exit(&p->p_lock);
if (ret_tidhash == NULL)
ret_tidhash = kmem_zalloc(sizeof (ret_tidhash_t),
KM_SLEEP);
if (old_dir != NULL)
kmem_free(old_dir, old_dirsz * sizeof (*old_dir));
if (old_hash != NULL)
kmem_free(old_hash, old_hashsz * sizeof (*old_hash));
new_dirsz = 2 * dirsz + 2;
new_dir = kmem_zalloc(new_dirsz * sizeof (lwpdir_t), KM_SLEEP);
for (ldp = new_dir, i = 1; i < new_dirsz; i++, ldp++)
ldp->ld_next = ldp + 1;
new_hashsz = (new_dirsz + 2) / 2;
new_hash = kmem_zalloc(new_hashsz * sizeof (tidhash_t),
KM_SLEEP);
mutex_enter(&p->p_lock);
if (p == curproc)
prbarrier(p);
if (dirsz != p->p_lwpdir_sz || p->p_lwpfree != NULL) {
old_dir = new_dir;
old_dirsz = new_dirsz;
old_hash = new_hash;
old_hashsz = new_hashsz;
} else {
old_hash = p->p_tidhash;
old_hashsz = p->p_tidhash_sz;
for (i = 0; i < old_hashsz; i++) {
mutex_enter(&old_hash[i].th_lock);
mutex_enter(&new_hash[i].th_lock);
}
old_dir = p->p_lwpdir;
old_dirsz = p->p_lwpdir_sz;
p->p_lwpdir = new_dir;
p->p_lwpfree = new_dir;
p->p_lwpdir_sz = new_dirsz;
for (ldp = old_dir, i = 0; i < old_dirsz; i++, ldp++)
lwp_hash_in(p, ldp->ld_entry,
new_hash, new_hashsz, 0);
ret_tidhash->rth_tidhash = old_hash;
ret_tidhash->rth_tidhash_sz = old_hashsz;
ret_tidhash->rth_next = p->p_ret_tidhash;
p->p_ret_tidhash = ret_tidhash;
membar_producer();
p->p_tidhash = new_hash;
membar_producer();
p->p_tidhash_sz = new_hashsz;
for (i = 0; i < old_hashsz; i++) {
mutex_exit(&old_hash[i].th_lock);
mutex_exit(&new_hash[i].th_lock);
}
ret_tidhash = NULL;
old_hash = NULL;
old_hashsz = 0;
}
}
if (p == curproc) {
prbarrier(p);
while ((curthread->t_proc_flag & TP_PRSTOP) &&
!ttolwp(curthread)->lwp_nostop) {
pool_barrier_exit();
prbarrier(p);
stop(PR_REQUESTED, 0);
pool_barrier_enter();
prbarrier(p);
}
if (p->p_flag & (SEXITLWPS|SKILLED)) {
err = 1;
goto error;
}
if (p->p_lwpfree == NULL)
goto grow;
}
kpreempt_disable();
if (CLASS_KERNEL(cid) && p != curproc) {
t->t_bind_cpu = binding = PBIND_NONE;
t->t_cpupart = oldpart = &cp_default;
t->t_bind_pset = PS_NONE;
t->t_bindflag = (uchar_t)default_binding_mode;
} else {
binding = curthread->t_bind_cpu;
t->t_bind_cpu = binding;
oldpart = t->t_cpupart;
t->t_cpupart = curthread->t_cpupart;
t->t_bind_pset = curthread->t_bind_pset;
t->t_bindflag = curthread->t_bindflag |
(uchar_t)default_binding_mode;
}
ASSERT(oldpart != NULL);
if (binding != PBIND_NONE && t->t_affinitycnt == 0) {
t->t_bound_cpu = cpu[binding];
if (t->t_lpl != t->t_bound_cpu->cpu_lpl)
lgrp_move_thread(t, t->t_bound_cpu->cpu_lpl, 1);
} else if (CLASS_KERNEL(cid)) {
lgrp_move_thread(t,
&t->t_cpupart->cp_lgrploads[LGRP_ROOTID], 1);
} else {
lgrp_move_thread(t, lgrp_choose(t, t->t_cpupart), 1);
}
kpreempt_enable();
ASSERT(t->t_lpl >= t->t_cpupart->cp_lgrploads);
ASSERT(t->t_lpl < t->t_cpupart->cp_lgrploads +
t->t_cpupart->cp_nlgrploads);
newkpj = p->p_task->tk_proj;
oldkpj = ttoproj(t);
if (newkpj != oldkpj) {
t->t_proj = newkpj;
(void) project_hold(newkpj);
project_rele(oldkpj);
}
if (cid != NOCLASS) {
if (p != curproc || curthread->t_cid != cid) {
err = CL_ENTERCLASS(t, cid, NULL, NULL, bufp);
t->t_pri = pri;
} else {
t->t_clfuncs = &(sclass[cid].cl_funcs->thread);
err = CL_FORK(curthread, t, bufp);
t->t_cid = cid;
}
if (err) {
atomic_inc_32(&p->p_zone->zone_ffmisc);
goto error;
} else {
bufp = NULL;
}
}
if (lwpid != 0)
t->t_tid = lwpid;
else {
id_t prev_id = p->p_lwpid;
do {
if (p->p_lwpid == INT_MAX) {
p->p_flag |= SLWPWRAP;
p->p_lwpid = 1;
}
if ((t->t_tid = ++p->p_lwpid) == prev_id) {
err = 1;
atomic_inc_32(&p->p_zone->zone_ffnoproc);
goto error;
}
if ((p->p_flag & SLWPWRAP) == 0)
break;
} while (lwp_hash_lookup(p, t->t_tid) != NULL);
}
if (PROC_IS_BRANDED(p)) {
if (BROP(p)->b_initlwp(lwp)) {
err = 1;
atomic_inc_32(&p->p_zone->zone_ffmisc);
goto error;
}
branded = 1;
}
if (t->t_tid == 1) {
kpreempt_disable();
ASSERT(t->t_lpl != NULL);
p->p_t1_lgrpid = t->t_lpl->lpl_lgrpid;
kpreempt_enable();
if (p->p_tr_lgrpid != LGRP_NONE &&
p->p_tr_lgrpid != p->p_t1_lgrpid) {
lgrp_update_trthr_migrations(1);
}
}
p->p_lwpcnt++;
t->t_waitfor = -1;
if (p->p_flag & SMSACCT)
t->t_proc_flag |= TP_MSACCT;
if (pr_watch_active(p))
watch_enable(t);
init_mstate(t, LMS_STOPPED);
t->t_proc_flag |= TP_HOLDLWP;
t->t_schedflag |= (TS_ALLSTART & ~(TS_CSTART | TS_CREATE));
t->t_whystop = PR_SUSPENDED;
t->t_whatstop = SUSPEND_NORMAL;
t->t_sig_check = 1;
t->t_pre_sys = 1;
t->t_post_sys = 1;
if ((tx = p->p_tlist) == NULL) {
t->t_back = t;
t->t_forw = t;
p->p_tlist = t;
} else {
t->t_forw = tx;
t->t_back = tx->t_back;
tx->t_back->t_forw = t;
tx->t_back = t;
}
lep->le_thread = t;
lep->le_lwpid = t->t_tid;
lep->le_start = t->t_start;
lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
lwp_fp_init(lwp);
if (state == TS_RUN) {
t->t_proc_flag &= ~TP_HOLDLWP;
lwp_create_done(t);
}
error:
if (err) {
if (CLASS_KERNEL(cid)) {
panic("Failed to create a system LWP");
}
kpreempt_disable();
lgrp_move_thread(t, NULL, 1);
kpreempt_enable();
ASSERT(MUTEX_HELD(&p->p_lock));
mutex_enter(&p->p_zone->zone_nlwps_lock);
p->p_task->tk_nlwps--;
p->p_task->tk_proj->kpj_nlwps--;
p->p_zone->zone_nlwps--;
mutex_exit(&p->p_zone->zone_nlwps_lock);
if (cid != NOCLASS && bufp != NULL)
CL_FREE(cid, bufp);
if (branded)
BROP(p)->b_freelwp(lwp);
mutex_exit(&p->p_lock);
t->t_state = TS_FREE;
thread_rele(t);
mutex_enter(&pidlock);
ASSERT(t != t->t_next);
t->t_next->t_prev = t->t_prev;
t->t_prev->t_next = t->t_next;
mutex_exit(&pidlock);
thread_free(t);
kmem_free(lep, sizeof (*lep));
lwp = NULL;
} else {
mutex_exit(&p->p_lock);
}
if (old_dir != NULL)
kmem_free(old_dir, old_dirsz * sizeof (*old_dir));
if (old_hash != NULL)
kmem_free(old_hash, old_hashsz * sizeof (*old_hash));
if (ret_tidhash != NULL)
kmem_free(ret_tidhash, sizeof (ret_tidhash_t));
DTRACE_PROC1(lwp__create, kthread_t *, t);
return (lwp);
}
void
lwp_create_done(kthread_t *t)
{
proc_t *p = ttoproc(t);
ASSERT(MUTEX_HELD(&p->p_lock));
thread_lock(t);
ASSERT(t->t_state == TS_STOPPED && !(t->t_schedflag & TS_CREATE));
if (!(t->t_schedflag & TS_CSTART))
p->p_lwprcnt++;
t->t_schedflag |= (TS_CSTART | TS_CREATE);
setrun_locked(t);
thread_unlock(t);
}
void
lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
{
int i;
for (i = 0; i < ct_ntypes; i++) {
dst->lwp_ct_active[i] = ctmpl_dup(src->lwp_ct_active[i]);
dst->lwp_ct_latest[i] = NULL;
}
}
void
lwp_ctmpl_clear(klwp_t *lwp)
{
ct_template_t *tmpl;
int i;
for (i = 0; i < ct_ntypes; i++) {
if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
ctmpl_free(tmpl);
lwp->lwp_ct_active[i] = NULL;
}
if (lwp->lwp_ct_latest[i] != NULL) {
contract_rele(lwp->lwp_ct_latest[i]);
lwp->lwp_ct_latest[i] = NULL;
}
}
}
void
lwp_exit(void)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
proc_t *p = ttoproc(t);
ASSERT(MUTEX_HELD(&p->p_lock));
mutex_exit(&p->p_lock);
#if defined(__sparc)
trash_user_windows();
#endif
tsd_exit();
kcpc_passivate();
pollcleanup();
if (t->t_door)
door_slam();
if (t->t_schedctl != NULL)
schedctl_lwp_cleanup(t);
if (t->t_upimutex != NULL)
upimutex_cleanup();
if (PROC_IS_BRANDED(p))
BROP(p)->b_lwpexit(lwp);
lwp_pcb_exit();
mutex_enter(&p->p_lock);
lwp_cleanup();
if (p->p_flag & SCOREDUMP)
stop(PR_SUSPENDED, SUSPEND_NORMAL);
prbarrier(p);
if (!(t->t_proc_flag & TP_DAEMON) &&
p->p_lwpcnt == p->p_lwpdaemon + 1) {
mutex_exit(&p->p_lock);
if (proc_exit(CLD_EXITED, 0) == 0) {
return;
}
mutex_enter(&p->p_lock);
ASSERT(curproc->p_flag & SEXITLWPS);
prbarrier(p);
}
DTRACE_PROC(lwp__exit);
prlwpexit(t);
if (!(t->t_proc_flag & TP_TWAIT) || (p->p_flag & SEXITLWPS))
lwp_hash_out(p, t->t_tid);
else {
ASSERT(!(t->t_proc_flag & TP_DAEMON));
p->p_lwpdir[t->t_dslot].ld_entry->le_thread = NULL;
p->p_zombcnt++;
cv_broadcast(&p->p_lwpexit);
}
if (t->t_proc_flag & TP_DAEMON) {
p->p_lwpdaemon--;
t->t_proc_flag &= ~TP_DAEMON;
}
t->t_proc_flag &= ~TP_TWAIT;
mutex_enter(&p->p_zone->zone_nlwps_lock);
p->p_task->tk_nlwps--;
p->p_task->tk_proj->kpj_nlwps--;
p->p_zone->zone_nlwps--;
mutex_exit(&p->p_zone->zone_nlwps_lock);
CL_EXIT(t);
ASSERT(p->p_lwpcnt != 0);
p->p_lwpcnt--;
if (p->p_lwpcnt == p->p_lwpdaemon + (p->p_lwpwait - p->p_lwpdwait))
cv_broadcast(&p->p_lwpexit);
t->t_proc_flag |= TP_LWPEXIT;
term_mstate(t);
t->t_forw->t_back = t->t_back;
t->t_back->t_forw = t->t_forw;
if (t == p->p_tlist)
p->p_tlist = t->t_forw;
if (t->t_sigqueue != NULL)
sigdelq(p, t, 0);
if (lwp->lwp_curinfo != NULL) {
siginfofree(lwp->lwp_curinfo);
lwp->lwp_curinfo = NULL;
}
if (lwp->lwp_spymaster != NULL) {
kmem_free(lwp->lwp_spymaster, sizeof (psinfo_t));
lwp->lwp_spymaster = NULL;
}
thread_rele(t);
t->t_preempt++;
if (t->t_ctx != NULL)
exitctx(t);
if (p->p_pctx != NULL)
exitpctx(p);
t->t_procp = &p0;
hat_thread_exit(t);
if (--p->p_lwprcnt == 0 || (t->t_proc_flag & TP_HOLDLWP) ||
(p->p_flag & SEXITLWPS))
cv_broadcast(&p->p_holdlwps);
mutex_exit(&p->p_lock);
mutex_enter(&pidlock);
ASSERT(t != t->t_next);
t->t_next->t_prev = t->t_prev;
t->t_prev->t_next = t->t_next;
cv_broadcast(&t->t_joincv);
mutex_exit(&pidlock);
t->t_state = TS_ZOMB;
swtch_from_zombie();
}
void
lwp_cleanup(void)
{
kthread_t *t = curthread;
proc_t *p = ttoproc(t);
ASSERT(MUTEX_HELD(&p->p_lock));
if (p->p_itimer != NULL)
timer_lwpexit();
if (t == p->p_agenttp) {
ASSERT(t->t_tid == p->p_lwpid);
p->p_lwpid--;
p->p_agenttp = NULL;
}
kpreempt_disable();
lgrp_move_thread(t, NULL, 1);
if (t->t_tid == 1) {
p->p_t1_lgrpid = LGRP_NONE;
}
kpreempt_enable();
lwp_ctmpl_clear(ttolwp(t));
}
int
lwp_suspend(kthread_t *t)
{
int tid;
proc_t *p = ttoproc(t);
ASSERT(MUTEX_HELD(&p->p_lock));
top:
t->t_proc_flag |= TP_HOLDLWP;
if (t == curthread) {
t->t_sig_check = 1;
} else {
thread_lock(t);
t->t_sig_check = 1;
if (ISWAKEABLE(t) || ISWAITING(t)) {
setrun_locked(t);
} else if (t->t_state == TS_ONPROC && t->t_cpu != CPU) {
poke_cpu(t->t_cpu->cpu_id);
}
tid = t->t_tid;
while (!SUSPENDED(t)) {
thread_unlock(t);
if (p->p_flag & SEXITLWPS)
lwp_exit();
if (!cv_wait_sig(&p->p_holdlwps, &p->p_lock))
return (EINTR);
if (idtot(p, tid) == NULL)
return (ESRCH);
thread_lock(t);
if ((t->t_proc_flag & TP_HOLDLWP) == 0) {
thread_unlock(t);
goto top;
}
}
thread_unlock(t);
}
return (0);
}
void
lwp_continue(kthread_t *t)
{
proc_t *p = ttoproc(t);
int was_suspended = t->t_proc_flag & TP_HOLDLWP;
ASSERT(MUTEX_HELD(&p->p_lock));
t->t_proc_flag &= ~TP_HOLDLWP;
thread_lock(t);
if (SUSPENDED(t) &&
!(p->p_flag & (SHOLDFORK | SHOLDFORK1 | SHOLDWATCH))) {
p->p_lwprcnt++;
t->t_schedflag |= TS_CSTART;
setrun_locked(t);
}
thread_unlock(t);
if (was_suspended)
cv_broadcast(&p->p_holdlwps);
}
void
holdlwp(void)
{
proc_t *p = curproc;
kthread_t *t = curthread;
mutex_enter(&p->p_lock);
if (!(p->p_flag & SCOREDUMP)) {
if ((p->p_flag & SEXITLWPS) || (t->t_proc_flag & TP_EXITLWP))
lwp_exit();
}
if (!(ISHOLD(p)) && !(p->p_flag & (SHOLDFORK1 | SHOLDWATCH))) {
mutex_exit(&p->p_lock);
return;
}
stop(PR_SUSPENDED, SUSPEND_NORMAL);
if (p->p_flag & SEXITLWPS)
lwp_exit();
mutex_exit(&p->p_lock);
}
int
holdlwps(int holdflag)
{
proc_t *p = curproc;
ASSERT(holdflag == SHOLDFORK || holdflag == SHOLDFORK1);
mutex_enter(&p->p_lock);
schedctl_finish_sigblock(curthread);
again:
while (p->p_flag & (SEXITLWPS | SHOLDFORK | SHOLDFORK1 | SHOLDWATCH)) {
if (p->p_flag & (SEXITLWPS | SHOLDFORK)) {
mutex_exit(&p->p_lock);
return (0);
}
stop(PR_SUSPENDED, SUSPEND_NORMAL);
}
p->p_flag |= holdflag;
pokelwps(p);
--p->p_lwprcnt;
while (p->p_lwprcnt > 0) {
if (p->p_flag & (SEXITLWPS | SHOLDWATCH)) {
p->p_lwprcnt++;
p->p_flag &= ~holdflag;
cv_broadcast(&p->p_holdlwps);
goto again;
}
if (p->p_stopsig | (curthread->t_proc_flag & TP_PRSTOP)) {
int whystop = p->p_stopsig? PR_JOBCONTROL :
PR_REQUESTED;
p->p_lwprcnt++;
p->p_flag &= ~holdflag;
stop(whystop, p->p_stopsig);
goto again;
}
cv_wait(&p->p_holdlwps, &p->p_lock);
}
p->p_lwprcnt++;
p->p_flag &= ~holdflag;
mutex_exit(&p->p_lock);
return (1);
}
static int
holdcheck(int clearflags)
{
proc_t *p = curproc;
if (p->p_flag & SEXITLWPS) {
p->p_lwprcnt++;
p->p_flag &= ~clearflags;
lwp_exit();
}
if (p->p_flag & SHOLDFORK1) {
p->p_lwprcnt++;
stop(PR_SUSPENDED, SUSPEND_NORMAL);
if (p->p_flag & SEXITLWPS) {
p->p_flag &= ~clearflags;
lwp_exit();
}
return (-1);
}
if (p->p_flag & SHOLDFORK) {
p->p_lwprcnt++;
while (p->p_flag & SHOLDFORK) {
p->p_flag |= SHOLDWATCH;
cv_broadcast(&p->p_holdlwps);
cv_wait(&p->p_holdlwps, &p->p_lock);
p->p_flag &= ~SHOLDWATCH;
}
return (-1);
}
return (0);
}
int
holdwatch(void)
{
proc_t *p = curproc;
kthread_t *t = curthread;
int ret = 0;
mutex_enter(&p->p_lock);
p->p_lwprcnt--;
if (holdcheck(0) != 0) {
mutex_exit(&p->p_lock);
return (-1);
}
if (!(p->p_flag & SHOLDWATCH)) {
p->p_flag |= SHOLDWATCH;
pokelwps(p);
while (pr_allstopped(p, 1) > 0) {
if (holdcheck(SHOLDWATCH) != 0) {
p->p_flag &= ~SHOLDWATCH;
mutex_exit(&p->p_lock);
return (-1);
}
cv_wait(&p->p_holdlwps, &p->p_lock);
}
p->p_flag |= SWATCHOK;
t->t_proc_flag &= ~TP_WATCHSTOP;
cv_broadcast(&p->p_holdlwps);
while (pr_allstopped(p, 0) > 0) {
if (holdcheck(SHOLDWATCH | SWATCHOK) != 0) {
p->p_flag &= ~(SHOLDWATCH | SWATCHOK);
mutex_exit(&p->p_lock);
return (-1);
}
cv_wait(&p->p_holdlwps, &p->p_lock);
}
p->p_flag &= ~SWATCHOK;
p->p_flag &= ~SHOLDWATCH;
p->p_lwprcnt++;
} else if (!(p->p_flag & SWATCHOK)) {
t->t_proc_flag |= TP_WATCHSTOP;
cv_broadcast(&p->p_holdlwps);
while (!(p->p_flag & SWATCHOK)) {
if (holdcheck(0) != 0) {
t->t_proc_flag &= ~TP_WATCHSTOP;
mutex_exit(&p->p_lock);
return (-1);
}
cv_wait(&p->p_holdlwps, &p->p_lock);
}
t->t_proc_flag &= ~TP_WATCHSTOP;
p->p_lwprcnt++;
stop(PR_SUSPENDED, SUSPEND_NORMAL);
ret = -1;
} else {
ASSERT(t->t_proc_flag & TP_STOPPING);
p->p_lwprcnt++;
}
mutex_exit(&p->p_lock);
return (ret);
}
void
pokelwps(proc_t *p)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&p->p_lock));
t = p->p_tlist;
do {
if (t == curthread)
continue;
thread_lock(t);
aston(t);
if (ISWAKEABLE(t) || ISWAITING(t)) {
setrun_locked(t);
} else if (t->t_state == TS_STOPPED) {
if (p->p_flag & SEXITLWPS) {
p->p_stopsig = 0;
t->t_schedflag |= (TS_XSTART | TS_PSTART);
setrun_locked(t);
}
if ((p->p_flag & SHOLDFORK) && SUSPENDED(t)) {
if ((t->t_schedflag & TS_CSTART) == 0) {
p->p_lwprcnt++;
t->t_schedflag |= TS_CSTART;
setrun_locked(t);
}
}
} else if (t->t_state == TS_ONPROC) {
if (t->t_cpu != CPU)
poke_cpu(t->t_cpu->cpu_id);
}
thread_unlock(t);
} while ((t = t->t_forw) != p->p_tlist);
}
void
runlwps(proc_t *p, ushort_t schedbits)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&p->p_lock));
p->p_stopsig = 0;
t = p->p_tlist;
do {
thread_lock(t);
if (t->t_state == TS_STOPPED) {
t->t_dtrace_stop = 0;
t->t_schedflag |= schedbits;
setrun_locked(t);
}
thread_unlock(t);
} while ((t = t->t_forw) != p->p_tlist);
}
void
continuelwps(proc_t *p)
{
kthread_t *t;
if (p->p_flag & SWATCHOK) {
ASSERT(curthread->t_proc_flag & TP_STOPPING);
return;
}
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT((p->p_flag & (SHOLDFORK | SHOLDFORK1 | SHOLDWATCH)) == 0);
t = p->p_tlist;
do {
thread_lock(t);
if (SUSPENDED(t) && !(t->t_proc_flag & TP_HOLDLWP)) {
p->p_lwprcnt++;
t->t_schedflag |= TS_CSTART;
setrun_locked(t);
}
thread_unlock(t);
} while ((t = t->t_forw) != p->p_tlist);
}
int
exitlwps(int coredump)
{
proc_t *p = curproc;
int heldcnt;
if (curthread->t_door)
door_slam();
if (p->p_door_list)
door_revoke_all();
if (curthread->t_schedctl != NULL)
schedctl_lwp_cleanup(curthread);
if (curthread->t_upimutex != NULL)
upimutex_cleanup();
mutex_enter(&p->p_lock);
prbarrier(p);
if (p->p_flag & SEXITLWPS) {
mutex_exit(&p->p_lock);
aston(curthread);
return (set_errno(EINTR));
}
p->p_flag |= SEXITLWPS;
if (coredump)
p->p_flag |= SCOREDUMP;
while (p->p_flag & (SHOLDFORK | SHOLDFORK1 | SHOLDWATCH)) {
cv_broadcast(&p->p_holdlwps);
cv_wait(&p->p_holdlwps, &p->p_lock);
prbarrier(p);
}
p->p_flag |= SHOLDFORK;
pokelwps(p);
--p->p_lwprcnt;
while (p->p_lwprcnt > 0) {
cv_wait(&p->p_holdlwps, &p->p_lock);
prbarrier(p);
}
p->p_lwprcnt++;
ASSERT(p->p_lwprcnt == 1);
if (coredump) {
p->p_flag &= ~(SCOREDUMP | SHOLDFORK | SEXITLWPS);
goto out;
}
p->p_flag &= ~SHOLDFORK;
if ((heldcnt = p->p_lwpcnt) > 1) {
kthread_t *t;
for (t = curthread->t_forw; --heldcnt > 0; t = t->t_forw) {
t->t_proc_flag &= ~TP_TWAIT;
lwp_continue(t);
}
}
--p->p_lwprcnt;
while (p->p_lwpcnt > 1) {
cv_wait(&p->p_holdlwps, &p->p_lock);
prbarrier(p);
}
++p->p_lwprcnt;
ASSERT(p->p_lwpcnt == 1 && p->p_lwprcnt == 1);
p->p_flag &= ~SEXITLWPS;
curthread->t_proc_flag &= ~TP_TWAIT;
out:
if (!coredump && p->p_zombcnt) {
lwpdir_t *ldp;
lwpent_t *lep;
int i;
for (ldp = p->p_lwpdir, i = 0; i < p->p_lwpdir_sz; i++, ldp++) {
lep = ldp->ld_entry;
if (lep != NULL && lep->le_thread != curthread) {
ASSERT(lep->le_thread == NULL);
p->p_zombcnt--;
lwp_hash_out(p, lep->le_lwpid);
}
}
ASSERT(p->p_zombcnt == 0);
}
curthread->t_proc_flag &= ~TP_HOLDLWP;
p->p_flag &= ~(SHOLDFORK | SHOLDFORK1 | SHOLDWATCH | SLWPWRAP);
mutex_exit(&p->p_lock);
return (0);
}
klwp_t *
forklwp(klwp_t *lwp, proc_t *cp, id_t lwpid)
{
klwp_t *clwp;
void *tregs, *tfpu;
kthread_t *t = lwptot(lwp);
kthread_t *ct;
proc_t *p = lwptoproc(lwp);
int cid;
void *bufp;
void *brand_data;
int val;
ASSERT(p == curproc);
ASSERT(t == curthread || (SUSPENDED(t) && lwp->lwp_asleep == 0));
#if defined(__sparc)
if (t == curthread)
(void) flush_user_windows_to_stack(NULL);
#endif
if (t == curthread)
(void) save_syscall_args();
clwp = lwp_create(cp->p_lwpcnt == 0 ? lwp_rtt_initial : lwp_rtt,
NULL, 0, cp, TS_STOPPED, t->t_pri, &t->t_hold, NOCLASS, lwpid);
if (clwp == NULL)
return (NULL);
ct = clwp->lwp_thread;
tregs = clwp->lwp_regs;
tfpu = clwp->lwp_fpu;
brand_data = clwp->lwp_brand;
mutex_enter(&cp->p_lock);
*clwp = *lwp;
init_mstate(ct, LMS_STOPPED);
bzero(&clwp->lwp_ru, sizeof (clwp->lwp_ru));
mutex_exit(&cp->p_lock);
clwp->lwp_pcb.pcb_flags = 0;
#if defined(__sparc)
clwp->lwp_pcb.pcb_step = STEP_NONE;
#endif
clwp->lwp_cursig = 0;
clwp->lwp_extsig = 0;
clwp->lwp_curinfo = (struct sigqueue *)0;
clwp->lwp_thread = ct;
ct->t_sysnum = t->t_sysnum;
clwp->lwp_regs = tregs;
clwp->lwp_fpu = tfpu;
clwp->lwp_brand = brand_data;
clwp->lwp_ap = clwp->lwp_arg;
clwp->lwp_procp = cp;
bzero(clwp->lwp_timer, sizeof (clwp->lwp_timer));
clwp->lwp_lastfault = 0;
clwp->lwp_lastfaddr = 0;
lwp_forkregs(lwp, clwp);
if (t->t_ctx)
forkctx(t, ct);
if (t->t_door)
door_fork(t, ct);
lwp_ctmpl_copy(clwp, lwp);
mutex_enter(&cp->p_lock);
if (!(t->t_proc_flag & TP_HOLDLWP))
ct->t_proc_flag &= ~TP_HOLDLWP;
if (cp->p_flag & SMSACCT)
ct->t_proc_flag |= TP_MSACCT;
mutex_exit(&cp->p_lock);
if (PROC_IS_BRANDED(p))
BROP(p)->b_forklwp(lwp, clwp);
retry:
cid = t->t_cid;
val = CL_ALLOC(&bufp, cid, KM_SLEEP);
ASSERT(val == 0);
mutex_enter(&p->p_lock);
if (cid != t->t_cid) {
mutex_exit(&p->p_lock);
CL_FREE(cid, bufp);
goto retry;
}
ct->t_unpark = t->t_unpark;
ct->t_clfuncs = t->t_clfuncs;
CL_FORK(t, ct, bufp);
ct->t_cid = t->t_cid;
mutex_exit(&p->p_lock);
return (clwp);
}
void
lwp_hash_in(proc_t *p, lwpent_t *lep, tidhash_t *tidhash, uint_t tidhash_sz,
int do_lock)
{
tidhash_t *thp = &tidhash[TIDHASH(lep->le_lwpid, tidhash_sz)];
lwpdir_t **ldpp;
lwpdir_t *ldp;
kthread_t *t;
ldp = p->p_lwpfree;
p->p_lwpfree = ldp->ld_next;
ASSERT(ldp->ld_entry == NULL);
ldp->ld_entry = lep;
if (do_lock)
mutex_enter(&thp->th_lock);
ldpp = &thp->th_list;
ldp->ld_next = *ldpp;
*ldpp = ldp;
if ((t = lep->le_thread) != NULL) {
ASSERT(lep->le_lwpid == t->t_tid);
t->t_dslot = (int)(ldp - p->p_lwpdir);
}
if (do_lock)
mutex_exit(&thp->th_lock);
}
void
lwp_hash_out(proc_t *p, id_t lwpid)
{
tidhash_t *thp = &p->p_tidhash[TIDHASH(lwpid, p->p_tidhash_sz)];
lwpdir_t **ldpp;
lwpdir_t *ldp;
lwpent_t *lep;
mutex_enter(&thp->th_lock);
for (ldpp = &thp->th_list;
(ldp = *ldpp) != NULL; ldpp = &ldp->ld_next) {
lep = ldp->ld_entry;
if (lep->le_lwpid == lwpid) {
prlwpfree(p, lep);
*ldpp = ldp->ld_next;
ldp->ld_entry = NULL;
ldp->ld_next = p->p_lwpfree;
p->p_lwpfree = ldp;
kmem_free(lep, sizeof (*lep));
break;
}
}
mutex_exit(&thp->th_lock);
}
lwpdir_t *
lwp_hash_lookup(proc_t *p, id_t lwpid)
{
tidhash_t *thp;
lwpdir_t *ldp;
if (p->p_tidhash == NULL)
return (NULL);
thp = &p->p_tidhash[TIDHASH(lwpid, p->p_tidhash_sz)];
for (ldp = thp->th_list; ldp != NULL; ldp = ldp->ld_next) {
if (ldp->ld_entry->le_lwpid == lwpid)
return (ldp);
}
return (NULL);
}
lwpdir_t *
lwp_hash_lookup_and_lock(proc_t *p, id_t lwpid, kmutex_t **mpp)
{
tidhash_t *tidhash;
uint_t tidhash_sz;
tidhash_t *thp;
lwpdir_t *ldp;
top:
tidhash_sz = p->p_tidhash_sz;
membar_consumer();
if ((tidhash = p->p_tidhash) == NULL)
return (NULL);
thp = &tidhash[TIDHASH(lwpid, tidhash_sz)];
mutex_enter(&thp->th_lock);
if (tidhash != p->p_tidhash || tidhash_sz != p->p_tidhash_sz) {
mutex_exit(&thp->th_lock);
goto top;
}
for (ldp = thp->th_list; ldp != NULL; ldp = ldp->ld_next) {
if (ldp->ld_entry->le_lwpid == lwpid) {
*mpp = &thp->th_lock;
return (ldp);
}
}
mutex_exit(&thp->th_lock);
return (NULL);
}
void
lwp_stat_update(lwp_stat_id_t lwp_stat_id, long inc)
{
klwp_t *lwp = ttolwp(curthread);
if (lwp == NULL)
return;
switch (lwp_stat_id) {
case LWP_STAT_INBLK:
lwp->lwp_ru.inblock += inc;
break;
case LWP_STAT_OUBLK:
lwp->lwp_ru.oublock += inc;
break;
case LWP_STAT_MSGRCV:
lwp->lwp_ru.msgrcv += inc;
break;
case LWP_STAT_MSGSND:
lwp->lwp_ru.msgsnd += inc;
break;
default:
panic("lwp_stat_update: invalid lwp_stat_id 0x%x", lwp_stat_id);
}
}