#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/stack.h>
#include <sys/pcb.h>
#include <sys/user.h>
#include <sys/systm.h>
#include <sys/sysinfo.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/cred.h>
#include <sys/resource.h>
#include <sys/task.h>
#include <sys/project.h>
#include <sys/proc.h>
#include <sys/debug.h>
#include <sys/disp.h>
#include <sys/class.h>
#include <vm/seg_kmem.h>
#include <vm/seg_kp.h>
#include <sys/machlock.h>
#include <sys/kmem.h>
#include <sys/varargs.h>
#include <sys/turnstile.h>
#include <sys/poll.h>
#include <sys/vtrace.h>
#include <sys/callb.h>
#include <c2/audit.h>
#include <sys/sobject.h>
#include <sys/cpupart.h>
#include <sys/pset.h>
#include <sys/door.h>
#include <sys/spl.h>
#include <sys/copyops.h>
#include <sys/rctl.h>
#include <sys/brand.h>
#include <sys/pool.h>
#include <sys/zone.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tndb.h>
#include <sys/cpc_impl.h>
#include <sys/sdt.h>
#include <sys/reboot.h>
#include <sys/kdi.h>
#include <sys/schedctl.h>
#include <sys/waitq.h>
#include <sys/cpucaps.h>
#include <sys/kiconv.h>
#include <sys/ctype.h>
#include <sys/smt.h>
struct kmem_cache *thread_cache;
struct kmem_cache *lwp_cache;
struct kmem_cache *turnstile_cache;
kthread_t *allthreads = &t0;
static kcondvar_t reaper_cv;
kthread_t *thread_deathrow;
kthread_t *lwp_deathrow;
kmutex_t reaplock;
int thread_reapcnt = 0;
int lwp_reapcnt = 0;
int reaplimit = 16;
thread_free_lock_t *thread_free_lock;
extern int nthread;
id_t syscid;
id_t sysdccid = CLASS_UNUSED;
void *segkp_thread;
int lwp_cache_sz = 32;
int t_cache_sz = 8;
static kt_did_t next_t_id = 1;
int default_binding_mode = TB_ALLHARD;
#define MAX_STKSIZE (32 * DEFAULTSTKSZ)
#define MIN_STKSIZE DEFAULTSTKSZ
int default_stksize;
int lwp_default_stksize;
static zone_key_t zone_thread_key;
unsigned int kmem_stackinfo;
kmem_stkinfo_t *kmem_stkinfo_log;
static kmutex_t kmem_stkinfo_lock;
static void *tsd_realloc(void *, size_t, size_t);
void thread_reaper(void);
static void stkinfo_begin(kthread_t *);
static void stkinfo_end(kthread_t *);
static size_t stkinfo_percent(caddr_t, caddr_t, caddr_t);
static int
turnstile_constructor(void *buf, void *cdrarg, int kmflags)
{
bzero(buf, sizeof (turnstile_t));
return (0);
}
static void
turnstile_destructor(void *buf, void *cdrarg)
{
turnstile_t *ts = buf;
ASSERT(ts->ts_free == NULL);
ASSERT(ts->ts_waiters == 0);
ASSERT(ts->ts_inheritor == NULL);
ASSERT(ts->ts_sleepq[0].sq_first == NULL);
ASSERT(ts->ts_sleepq[1].sq_first == NULL);
}
void
thread_init(void)
{
kthread_t *tp;
extern char sys_name[];
extern void idle();
struct cpu *cpu = CPU;
int i;
kmutex_t *lp;
mutex_init(&reaplock, NULL, MUTEX_SPIN, (void *)ipltospl(DISP_LEVEL));
thread_free_lock =
kmem_alloc(sizeof (thread_free_lock_t) * THREAD_FREE_NUM, KM_SLEEP);
for (i = 0; i < THREAD_FREE_NUM; i++) {
lp = &thread_free_lock[i].tf_lock;
mutex_init(lp, NULL, MUTEX_DEFAULT, NULL);
}
#if defined(__x86)
thread_cache = kmem_cache_create("thread_cache", sizeof (kthread_t),
PTR24_ALIGN, NULL, NULL, NULL, NULL, NULL, 0);
lwp_cache = kmem_cache_create("lwp_cache", sizeof (klwp_t),
64, NULL, NULL, NULL, NULL, NULL, 0);
#else
thread_cache = kmem_cache_create("thread_cache", sizeof (kthread_t),
PTR24_ALIGN, NULL, NULL, NULL, NULL, static_arena, 0);
lwp_stk_cache_init();
lwp_cache = kmem_cache_create("lwp_cache", sizeof (klwp_t),
0, NULL, NULL, NULL, NULL, NULL, 0);
#endif
turnstile_cache = kmem_cache_create("turnstile_cache",
sizeof (turnstile_t), 0,
turnstile_constructor, turnstile_destructor, NULL, NULL, NULL, 0);
label_init();
cred_init();
rctl_init();
cpucaps_init();
zone_init();
project_init();
brand_init();
kiconv_init();
task_init();
tcache_init();
pool_init();
curthread->t_ts = kmem_cache_alloc(turnstile_cache, KM_SLEEP);
if (default_stksize == 0) {
default_stksize = DEFAULTSTKSZ;
} else if (default_stksize % PAGESIZE != 0 ||
default_stksize > MAX_STKSIZE ||
default_stksize < MIN_STKSIZE) {
cmn_err(CE_WARN, "Illegal stack size. Using %d",
(int)DEFAULTSTKSZ);
default_stksize = DEFAULTSTKSZ;
} else {
lwp_default_stksize = default_stksize;
}
if (lwp_default_stksize == 0) {
lwp_default_stksize = default_stksize;
} else if (lwp_default_stksize % PAGESIZE != 0 ||
lwp_default_stksize > MAX_STKSIZE ||
lwp_default_stksize < MIN_STKSIZE) {
cmn_err(CE_WARN, "Illegal stack size. Using %d",
default_stksize);
lwp_default_stksize = default_stksize;
}
segkp_lwp = segkp_cache_init(segkp, lwp_cache_sz,
lwp_default_stksize,
(KPD_NOWAIT | KPD_HASREDZONE | KPD_LOCKED));
segkp_thread = segkp_cache_init(segkp, t_cache_sz,
default_stksize, KPD_HASREDZONE | KPD_LOCKED | KPD_NO_ANON);
(void) getcid(sys_name, &syscid);
curthread->t_cid = syscid;
tp = thread_create(NULL, 0, idle, NULL, 0, &p0, TS_STOPPED, -1);
cpu->cpu_idle_thread = tp;
tp->t_preempt = 1;
tp->t_disp_queue = cpu->cpu_disp;
ASSERT(tp->t_disp_queue != NULL);
tp->t_bound_cpu = cpu;
tp->t_affinitycnt = 1;
CALLB_CPR_INIT_SAFE(tp, "idle");
(void) thread_create(NULL, 0, (void (*)())thread_reaper,
NULL, 0, &p0, TS_RUN, minclsyspri);
kmem_thread_init();
if (boothowto & RB_DEBUG)
kdi_dvec_thravail();
}
kthread_t *
thread_create(
caddr_t stk,
size_t stksize,
void (*proc)(),
void *arg,
size_t len,
proc_t *pp,
int state,
pri_t pri)
{
kthread_t *t;
extern struct classfuncs sys_classfuncs;
turnstile_t *ts;
ts = kmem_cache_alloc(turnstile_cache, KM_SLEEP);
if (stk == NULL) {
if (stksize < default_stksize)
stksize = default_stksize;
if (stksize == default_stksize) {
stk = (caddr_t)segkp_cache_get(segkp_thread);
} else {
stksize = roundup(stksize, PAGESIZE);
stk = (caddr_t)segkp_get(segkp, stksize,
(KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED));
}
ASSERT(stk != NULL);
if (stksize <= sizeof (kthread_t) + PTR24_ALIGN)
cmn_err(CE_PANIC, "thread_create: proposed stack size"
" too small to hold thread.");
#ifdef STACK_GROWTH_DOWN
stksize -= SA(sizeof (kthread_t) + PTR24_ALIGN - 1);
stksize &= -PTR24_ALIGN;
t = (kthread_t *)(stk + stksize);
bzero(t, sizeof (kthread_t));
if (audit_active)
audit_thread_create(t);
t->t_stk = stk + stksize;
t->t_stkbase = stk;
#else
stksize -= SA(sizeof (kthread_t));
t = (kthread_t *)(stk);
bzero(t, sizeof (kthread_t));
t->t_stk = stk + sizeof (kthread_t);
t->t_stkbase = stk + stksize + sizeof (kthread_t);
#endif
t->t_flag |= T_TALLOCSTK;
t->t_swap = stk;
} else {
t = kmem_cache_alloc(thread_cache, KM_SLEEP);
bzero(t, sizeof (kthread_t));
ASSERT(((uintptr_t)t & (PTR24_ALIGN - 1)) == 0);
if (audit_active)
audit_thread_create(t);
#ifdef STACK_GROWTH_DOWN
t->t_stk = stk + stksize;
t->t_stkbase = stk;
#else
t->t_stk = stk;
t->t_stkbase = stk + stksize;
#endif
}
if (kmem_stackinfo != 0) {
stkinfo_begin(t);
}
t->t_ts = ts;
mutex_enter(&pp->p_crlock);
if (pp->p_cred)
crhold(t->t_cred = pp->p_cred);
mutex_exit(&pp->p_crlock);
t->t_start = gethrestime_sec();
t->t_startpc = proc;
t->t_procp = pp;
t->t_clfuncs = &sys_classfuncs.thread;
t->t_cid = syscid;
t->t_pri = pri;
t->t_stime = ddi_get_lbolt();
t->t_schedflag = TS_LOAD | TS_DONT_SWAP;
t->t_bind_cpu = PBIND_NONE;
t->t_bindflag = (uchar_t)default_binding_mode;
t->t_bind_pset = PS_NONE;
t->t_plockp = &pp->p_lock;
t->t_copyops = NULL;
t->t_taskq = NULL;
t->t_anttime = 0;
t->t_hatdepth = 0;
t->t_dtrace_vtime = 1;
CPU_STATS_ADDQ(CPU, sys, nthreads, 1);
LOCK_INIT_CLEAR(&t->t_lock);
if (proc != NULL) {
t->t_stk = thread_stk_init(t->t_stk);
thread_load(t, proc, arg, len);
}
t->t_proj = project_hold(proj0p);
lgrp_affinity_init(&t->t_lgrp_affinity);
mutex_enter(&pidlock);
nthread++;
t->t_did = next_t_id++;
t->t_prev = curthread->t_prev;
t->t_next = curthread;
kpreempt_disable();
curthread->t_prev->t_next = t;
curthread->t_prev = t;
t->t_cpupart = &cp_default;
lgrp_move_thread(t, &cp_default.cp_lgrploads[LGRP_ROOTID], 1);
if (CPU->cpu_part == &cp_default) {
t->t_cpu = CPU;
} else {
t->t_cpu = cp_default.cp_cpulist;
t->t_cpu = disp_lowpri_cpu(t->t_cpu, t, t->t_pri);
}
t->t_disp_queue = t->t_cpu->cpu_disp;
kpreempt_enable();
switch (state) {
case TS_RUN:
curthread->t_oldspl = splhigh();
THREAD_SET_STATE(t, TS_STOPPED, &transition_lock);
CL_SETRUN(t);
thread_unlock(t);
break;
case TS_ONPROC:
THREAD_ONPROC(t, t->t_cpu);
break;
case TS_FREE:
THREAD_FREEINTR(t, CPU);
break;
case TS_STOPPED:
THREAD_SET_STATE(t, TS_STOPPED, &stop_lock);
break;
default:
cmn_err(CE_PANIC, "thread_create: invalid state %d", state);
}
mutex_exit(&pidlock);
return (t);
}
void
thread_rele(kthread_t *t)
{
kproject_t *kpj;
thread_lock(t);
ASSERT(t == curthread || t->t_state == TS_FREE || t->t_procp == &p0);
kpj = ttoproj(t);
t->t_proj = proj0p;
thread_unlock(t);
if (kpj != proj0p) {
project_rele(kpj);
(void) project_hold(proj0p);
}
}
void
thread_exit(void)
{
kthread_t *t = curthread;
if ((t->t_proc_flag & TP_ZTHREAD) != 0)
cmn_err(CE_PANIC, "thread_exit: zthread_exit() not called");
tsd_exit();
kcpc_passivate();
ASSERT(t->t_pollstate == NULL);
ASSERT(t->t_schedctl == NULL);
if (t->t_door)
door_slam();
thread_rele(t);
t->t_preempt++;
mutex_enter(&pidlock);
t->t_next->t_prev = t->t_prev;
t->t_prev->t_next = t->t_next;
ASSERT(allthreads != t);
cv_broadcast(&t->t_joincv);
mutex_exit(&pidlock);
if (t->t_ctx != NULL)
exitctx(t);
if (t->t_procp->p_pctx != NULL)
exitpctx(t->t_procp);
if (kmem_stackinfo != 0) {
stkinfo_end(t);
}
t->t_state = TS_ZOMB;
swtch_from_zombie();
}
static kthread_t *
did_to_thread(kt_did_t tid)
{
kthread_t *t;
ASSERT(MUTEX_HELD(&pidlock));
for (t = curthread->t_next; t != curthread; t = t->t_next) {
if (t->t_did == tid)
break;
}
if (t->t_did == tid)
return (t);
else
return (NULL);
}
void
thread_join(kt_did_t tid)
{
kthread_t *t;
ASSERT(tid != curthread->t_did);
ASSERT(tid != t0.t_did);
mutex_enter(&pidlock);
while (t = did_to_thread(tid))
cv_wait(&t->t_joincv, &pidlock);
mutex_exit(&pidlock);
}
void
thread_free_prevent(kthread_t *t)
{
kmutex_t *lp;
lp = &thread_free_lock[THREAD_FREE_HASH(t)].tf_lock;
mutex_enter(lp);
}
void
thread_free_allow(kthread_t *t)
{
kmutex_t *lp;
lp = &thread_free_lock[THREAD_FREE_HASH(t)].tf_lock;
mutex_exit(lp);
}
static void
thread_free_barrier(kthread_t *t)
{
kmutex_t *lp;
lp = &thread_free_lock[THREAD_FREE_HASH(t)].tf_lock;
mutex_enter(lp);
mutex_exit(lp);
}
void
thread_free(kthread_t *t)
{
boolean_t allocstk = (t->t_flag & T_TALLOCSTK);
klwp_t *lwp = t->t_lwp;
caddr_t swap = t->t_swap;
ASSERT(t != &t0 && t->t_state == TS_FREE);
ASSERT(t->t_door == NULL);
ASSERT(t->t_schedctl == NULL);
ASSERT(t->t_pollstate == NULL);
t->t_pri = 0;
t->t_pc = 0;
t->t_sp = 0;
t->t_wchan0 = NULL;
t->t_wchan = NULL;
if (t->t_cred != NULL) {
crfree(t->t_cred);
t->t_cred = 0;
}
if (t->t_pdmsg) {
kmem_free(t->t_pdmsg, strlen(t->t_pdmsg) + 1);
t->t_pdmsg = NULL;
}
if (audit_active)
audit_thread_free(t);
if (t->t_cldata) {
CL_EXITCLASS(t->t_cid, (caddr_t *)t->t_cldata);
}
if (t->t_rprof != NULL) {
kmem_free(t->t_rprof, sizeof (*t->t_rprof));
t->t_rprof = NULL;
}
t->t_lockp = NULL;
if (lwp)
lwp_freeregs(lwp, 0);
if (t->t_ctx)
freectx(t, 0);
t->t_stk = NULL;
if (lwp)
lwp_stk_fini(lwp);
lock_clear(&t->t_lock);
if (t->t_ts->ts_waiters > 0)
panic("thread_free: turnstile still active");
kmem_cache_free(turnstile_cache, t->t_ts);
free_afd(&t->t_activefd);
thread_free_barrier(t);
ASSERT(ttoproj(t) == proj0p);
project_rele(ttoproj(t));
lgrp_affinity_free(&t->t_lgrp_affinity);
mutex_enter(&pidlock);
nthread--;
mutex_exit(&pidlock);
if (t->t_name != NULL) {
kmem_free(t->t_name, THREAD_NAME_MAX);
t->t_name = NULL;
}
t->t_lwp = NULL;
t->t_swap = NULL;
if (swap) {
segkp_release(segkp, swap);
}
if (lwp) {
kmem_cache_free(lwp_cache, lwp);
}
if (!allocstk) {
kmem_cache_free(thread_cache, t);
}
}
static kthread_t *
thread_zone_cleanup(kthread_t **tp, int *countp, zoneid_t zoneid)
{
kthread_t *tmp, *list = NULL;
cred_t *cr;
ASSERT(MUTEX_HELD(&reaplock));
while (*tp != NULL) {
if ((cr = (*tp)->t_cred) != NULL && crgetzoneid(cr) == zoneid) {
tmp = *tp;
*tp = tmp->t_forw;
tmp->t_forw = list;
list = tmp;
(*countp)--;
} else {
tp = &(*tp)->t_forw;
}
}
return (list);
}
static void
thread_reap_list(kthread_t *t)
{
kthread_t *next;
while (t != NULL) {
next = t->t_forw;
thread_free(t);
t = next;
}
}
static void
thread_zone_destroy(zoneid_t zoneid, void *unused)
{
kthread_t *t, *l;
mutex_enter(&reaplock);
t = thread_zone_cleanup(&thread_deathrow, &thread_reapcnt, zoneid);
l = thread_zone_cleanup(&lwp_deathrow, &lwp_reapcnt, zoneid);
mutex_exit(&reaplock);
mutex_sync();
thread_reap_list(t);
thread_reap_list(l);
}
void
thread_reaper()
{
kthread_t *t, *l;
callb_cpr_t cprinfo;
zone_key_create(&zone_thread_key, NULL, NULL, thread_zone_destroy);
CALLB_CPR_INIT(&cprinfo, &reaplock, callb_generic_cpr, "t_reaper");
for (;;) {
mutex_enter(&reaplock);
while (thread_deathrow == NULL && lwp_deathrow == NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&reaper_cv, &reaplock);
CALLB_CPR_SAFE_END(&cprinfo, &reaplock);
}
t = thread_deathrow;
l = lwp_deathrow;
thread_deathrow = NULL;
lwp_deathrow = NULL;
thread_reapcnt = 0;
lwp_reapcnt = 0;
mutex_exit(&reaplock);
mutex_sync();
thread_reap_list(t);
thread_reap_list(l);
delay(hz);
}
}
void
reapq_move_lq_to_tq(kthread_t *t)
{
ASSERT(t->t_state == TS_FREE);
ASSERT(MUTEX_HELD(&reaplock));
t->t_forw = thread_deathrow;
thread_deathrow = t;
thread_reapcnt++;
if (lwp_reapcnt + thread_reapcnt > reaplimit)
cv_signal(&reaper_cv);
}
void
reapq_add(kthread_t *t)
{
mutex_enter(&reaplock);
if (t->t_flag & T_LWPREUSE) {
ASSERT(ttolwp(t) != NULL);
t->t_forw = lwp_deathrow;
lwp_deathrow = t;
lwp_reapcnt++;
} else {
t->t_forw = thread_deathrow;
thread_deathrow = t;
thread_reapcnt++;
}
if (lwp_reapcnt + thread_reapcnt > reaplimit)
cv_signal(&reaper_cv);
t->t_state = TS_FREE;
lock_clear(&t->t_lock);
thread_lock(t);
thread_unlock(t);
mutex_exit(&reaplock);
}
static struct ctxop *
ctxop_find_by_tmpl(kthread_t *t, const struct ctxop_template *ct, void *arg)
{
struct ctxop *ctx, *head;
ASSERT(MUTEX_HELD(&t->t_ctx_lock));
ASSERT(curthread->t_preempt > 0);
if (t->t_ctx == NULL) {
return (NULL);
}
ctx = head = t->t_ctx;
do {
if (ctx->save_op == ct->ct_save &&
ctx->restore_op == ct->ct_restore &&
ctx->fork_op == ct->ct_fork &&
ctx->lwp_create_op == ct->ct_lwp_create &&
ctx->exit_op == ct->ct_exit &&
ctx->free_op == ct->ct_free &&
ctx->arg == arg) {
return (ctx);
}
ctx = ctx->next;
} while (ctx != head);
return (NULL);
}
static void
ctxop_detach_chain(kthread_t *t, struct ctxop *ctx)
{
ASSERT(t != NULL);
ASSERT(t->t_ctx != NULL);
ASSERT(ctx != NULL);
ASSERT(ctx->next != NULL && ctx->prev != NULL);
ctx->prev->next = ctx->next;
ctx->next->prev = ctx->prev;
if (ctx->next == ctx) {
t->t_ctx = NULL;
} else if (ctx == t->t_ctx) {
t->t_ctx = ctx->next;
}
ctx->next = ctx->prev = NULL;
}
struct ctxop *
ctxop_allocate(const struct ctxop_template *ct, void *arg)
{
struct ctxop *ctx;
VERIFY3U(ct->ct_rev, ==, CTXOP_TPL_REV);
ctx = kmem_alloc(sizeof (struct ctxop), KM_SLEEP);
ctx->save_op = ct->ct_save;
ctx->restore_op = ct->ct_restore;
ctx->fork_op = ct->ct_fork;
ctx->lwp_create_op = ct->ct_lwp_create;
ctx->exit_op = ct->ct_exit;
ctx->free_op = ct->ct_free;
ctx->arg = arg;
ctx->save_ts = 0;
ctx->restore_ts = 0;
ctx->next = ctx->prev = NULL;
return (ctx);
}
void
ctxop_free(struct ctxop *ctx)
{
if (ctx->free_op != NULL)
(ctx->free_op)(ctx->arg, 0);
kmem_free(ctx, sizeof (struct ctxop));
}
void
ctxop_attach(kthread_t *t, struct ctxop *ctx)
{
ASSERT(ctx->next == NULL && ctx->prev == NULL);
kpreempt_disable();
if (t->t_ctx == NULL) {
ctx->next = ctx;
ctx->prev = ctx;
} else {
struct ctxop *head = t->t_ctx, *tail = t->t_ctx->prev;
ctx->next = head;
ctx->prev = tail;
head->prev = ctx;
tail->next = ctx;
}
t->t_ctx = ctx;
kpreempt_enable();
}
void
ctxop_detach(kthread_t *t, struct ctxop *ctx)
{
ASSERT(t == curthread || ttoproc(t)->p_stat == SIDL ||
ttoproc(t)->p_agenttp == curthread || t->t_state == TS_STOPPED);
mutex_enter(&t->t_ctx_lock);
kpreempt_disable();
VERIFY(t->t_ctx != NULL);
#ifdef DEBUG
struct ctxop *head, *cur;
head = cur = t->t_ctx;
for (;;) {
if (cur == ctx) {
break;
}
cur = cur->next;
ASSERT3P(cur, !=, head);
}
#endif
ctxop_detach_chain(t, ctx);
mutex_exit(&t->t_ctx_lock);
kpreempt_enable();
}
void
ctxop_install(kthread_t *t, const struct ctxop_template *ct, void *arg)
{
ctxop_attach(t, ctxop_allocate(ct, arg));
}
int
ctxop_remove(kthread_t *t, const struct ctxop_template *ct, void *arg)
{
struct ctxop *ctx;
ASSERT(t == curthread || ttoproc(t)->p_stat == SIDL ||
ttoproc(t)->p_agenttp == curthread || t->t_state == TS_STOPPED);
mutex_enter(&t->t_ctx_lock);
kpreempt_disable();
ctx = ctxop_find_by_tmpl(t, ct, arg);
if (ctx != NULL) {
ctxop_detach_chain(t, ctx);
ctxop_free(ctx);
}
mutex_exit(&t->t_ctx_lock);
kpreempt_enable();
if (ctx != NULL) {
return (1);
}
return (0);
}
void
savectx(kthread_t *t)
{
ASSERT(t == curthread);
if (t->t_ctx != NULL) {
struct ctxop *ctx, *head;
ctx = head = t->t_ctx;
do {
if (ctx->save_op != NULL) {
ctx->save_ts = gethrtime_unscaled();
(ctx->save_op)(ctx->arg);
}
ctx = ctx->next;
} while (ctx != head);
}
}
void
restorectx(kthread_t *t)
{
ASSERT(t == curthread);
if (t->t_ctx != NULL) {
struct ctxop *ctx, *tail;
ctx = tail = t->t_ctx->prev;
do {
if (ctx->restore_op != NULL) {
ctx->restore_ts = gethrtime_unscaled();
(ctx->restore_op)(ctx->arg);
}
ctx = ctx->prev;
} while (ctx != tail);
}
}
void
forkctx(kthread_t *t, kthread_t *ct)
{
if (t->t_ctx != NULL) {
struct ctxop *ctx, *head;
ctx = head = t->t_ctx;
do {
if (ctx->fork_op != NULL) {
(ctx->fork_op)(t, ct);
}
ctx = ctx->next;
} while (ctx != head);
}
}
void
lwp_createctx(kthread_t *t, kthread_t *ct)
{
if (t->t_ctx != NULL) {
struct ctxop *ctx, *head;
ctx = head = t->t_ctx;
do {
if (ctx->lwp_create_op != NULL) {
(ctx->lwp_create_op)(t, ct);
}
ctx = ctx->next;
} while (ctx != head);
}
}
void
exitctx(kthread_t *t)
{
if (t->t_ctx != NULL) {
struct ctxop *ctx, *head;
ctx = head = t->t_ctx;
do {
if (ctx->exit_op != NULL) {
(ctx->exit_op)(t);
}
ctx = ctx->next;
} while (ctx != head);
}
}
void
freectx(kthread_t *t, int isexec)
{
kpreempt_disable();
if (t->t_ctx != NULL) {
struct ctxop *ctx, *head;
ctx = head = t->t_ctx;
t->t_ctx = NULL;
do {
struct ctxop *next = ctx->next;
if (ctx->free_op != NULL) {
(ctx->free_op)(ctx->arg, isexec);
}
kmem_free(ctx, sizeof (struct ctxop));
ctx = next;
} while (ctx != head);
}
kpreempt_enable();
}
void
freectx_ctx(struct ctxop *ctx)
{
struct ctxop *head = ctx;
ASSERT(ctx != NULL);
kpreempt_disable();
head = ctx;
do {
struct ctxop *next = ctx->next;
if (ctx->free_op != NULL) {
(ctx->free_op)(ctx->arg, 0);
}
kmem_free(ctx, sizeof (struct ctxop));
ctx = next;
} while (ctx != head);
kpreempt_enable();
}
void
setrun_locked(kthread_t *t)
{
ASSERT(THREAD_LOCK_HELD(t));
if (t->t_state == TS_SLEEP) {
SOBJ_UNSLEEP(t->t_sobj_ops, t);
} else if (t->t_state & (TS_RUN | TS_ONPROC)) {
return;
} else if (t->t_state == TS_WAIT) {
waitq_setrun(t);
} else if (t->t_state == TS_STOPPED) {
if ((t->t_schedflag & TS_ALLSTART) != TS_ALLSTART)
return;
t->t_whystop = 0;
t->t_whatstop = 0;
t->t_schedflag &= ~TS_ALLSTART;
THREAD_TRANSITION(t);
ASSERT(t->t_lockp == &transition_lock);
ASSERT(t->t_wchan0 == NULL && t->t_wchan == NULL);
CL_SETRUN(t);
}
}
void
setrun(kthread_t *t)
{
thread_lock(t);
setrun_locked(t);
thread_unlock(t);
}
kthread_t *
thread_unpin()
{
kthread_t *t = curthread;
kthread_t *itp;
int i;
extern int intr_passivate();
ASSERT(t->t_intr != NULL);
itp = t->t_intr;
t->t_intr = NULL;
smt_end_intr();
i = intr_passivate(t, itp);
TRACE_5(TR_FAC_INTR, TR_INTR_PASSIVATE,
"intr_passivate:level %d curthread %p (%T) ithread %p (%T)",
i, t, t, itp, itp);
t->t_lwp = NULL;
#if DEBUG
if (i < 0 || i > LOCK_LEVEL)
cmn_err(CE_PANIC, "thread_unpin: ipl out of range %x", i);
#endif
ASSERT(CPU->cpu_intr_actv & (1 << i));
set_base_spl();
return (itp);
}
void
thread_create_intr(struct cpu *cp)
{
kthread_t *tp;
tp = thread_create(NULL, 0,
(void (*)())thread_create_intr, NULL, 0, &p0, TS_ONPROC, 0);
THREAD_FREEINTR(tp, cp);
tp->t_cred = NULL;
tp->t_flag |= T_INTR_THREAD;
tp->t_cpu = cp;
tp->t_bound_cpu = cp;
tp->t_disp_queue = cp->cpu_disp;
tp->t_affinitycnt = 1;
tp->t_preempt = 1;
tp->t_bind_cpu = PBIND_NONE;
tp->t_bind_pset = PS_NONE;
#if defined(__x86)
tp->t_stk -= STACK_ALIGN;
*(tp->t_stk) = 0;
#endif
tp->t_link = cp->cpu_intr_thread;
cp->cpu_intr_thread = tp;
}
static kmutex_t tsd_mutex;
static uint_t tsd_nkeys;
static void (**tsd_destructor)(void *);
static struct tsd_thread *tsd_list;
void
tsd_defaultdestructor(void *value)
{}
void
tsd_create(uint_t *keyp, void (*destructor)(void *))
{
int i;
uint_t nkeys;
mutex_enter(&tsd_mutex);
if (*keyp) {
mutex_exit(&tsd_mutex);
return;
}
if (destructor == NULL)
destructor = tsd_defaultdestructor;
for (i = 0; i < tsd_nkeys; ++i)
if (tsd_destructor[i] == NULL)
break;
if (i == tsd_nkeys) {
if ((nkeys = (tsd_nkeys << 1)) == 0)
nkeys = 1;
tsd_destructor =
(void (**)(void *))tsd_realloc((void *)tsd_destructor,
(size_t)(tsd_nkeys * sizeof (void (*)(void *))),
(size_t)(nkeys * sizeof (void (*)(void *))));
tsd_nkeys = nkeys;
}
tsd_destructor[i] = destructor;
*keyp = i + 1;
mutex_exit(&tsd_mutex);
}
void
tsd_destroy(uint_t *keyp)
{
uint_t key;
struct tsd_thread *tsd;
mutex_enter(&tsd_mutex);
key = *keyp;
*keyp = 0;
ASSERT(key <= tsd_nkeys);
if (key != 0) {
uint_t k = key - 1;
for (tsd = tsd_list; tsd; tsd = tsd->ts_next) {
if (key > tsd->ts_nkeys)
continue;
if (tsd->ts_value[k] && tsd_destructor[k])
(*tsd_destructor[k])(tsd->ts_value[k]);
tsd->ts_value[k] = NULL;
}
tsd_destructor[k] = NULL;
}
mutex_exit(&tsd_mutex);
}
void *
tsd_get(uint_t key)
{
return (tsd_agent_get(curthread, key));
}
int
tsd_set(uint_t key, void *value)
{
return (tsd_agent_set(curthread, key, value));
}
void *
tsd_agent_get(kthread_t *t, uint_t key)
{
struct tsd_thread *tsd = t->t_tsd;
ASSERT(t == curthread ||
ttoproc(t)->p_agenttp == curthread || t->t_state == TS_STOPPED);
if (key && tsd != NULL && key <= tsd->ts_nkeys)
return (tsd->ts_value[key - 1]);
return (NULL);
}
int
tsd_agent_set(kthread_t *t, uint_t key, void *value)
{
struct tsd_thread *tsd = t->t_tsd;
ASSERT(t == curthread ||
ttoproc(t)->p_agenttp == curthread || t->t_state == TS_STOPPED);
if (key == 0)
return (EINVAL);
if (tsd == NULL)
tsd = t->t_tsd = kmem_zalloc(sizeof (*tsd), KM_SLEEP);
if (key <= tsd->ts_nkeys) {
tsd->ts_value[key - 1] = value;
return (0);
}
ASSERT(key <= tsd_nkeys);
mutex_enter(&tsd_mutex);
if (tsd->ts_nkeys == 0) {
if ((tsd->ts_next = tsd_list) != NULL)
tsd_list->ts_prev = tsd;
tsd_list = tsd;
}
tsd->ts_value = tsd_realloc(tsd->ts_value,
tsd->ts_nkeys * sizeof (void *),
key * sizeof (void *));
tsd->ts_nkeys = key;
tsd->ts_value[key - 1] = value;
mutex_exit(&tsd_mutex);
return (0);
}
void *
tsd_getcreate(uint_t *keyp, void (*destroy)(void *), void *(*allocate)(void))
{
void *value;
uint_t key = *keyp;
struct tsd_thread *tsd = curthread->t_tsd;
if (tsd == NULL)
tsd = curthread->t_tsd = kmem_zalloc(sizeof (*tsd), KM_SLEEP);
if (key && key <= tsd->ts_nkeys && (value = tsd->ts_value[key - 1]))
return (value);
if (key == 0)
tsd_create(keyp, destroy);
(void) tsd_set(*keyp, value = (*allocate)());
return (value);
}
void
tsd_exit(void)
{
int i;
struct tsd_thread *tsd = curthread->t_tsd;
if (tsd == NULL)
return;
if (tsd->ts_nkeys == 0) {
kmem_free(tsd, sizeof (*tsd));
curthread->t_tsd = NULL;
return;
}
mutex_enter(&tsd_mutex);
for (i = 0; i < tsd->ts_nkeys; i++) {
if (tsd->ts_value[i] && tsd_destructor[i])
(*tsd_destructor[i])(tsd->ts_value[i]);
tsd->ts_value[i] = NULL;
}
if (tsd->ts_next)
tsd->ts_next->ts_prev = tsd->ts_prev;
if (tsd->ts_prev)
tsd->ts_prev->ts_next = tsd->ts_next;
if (tsd_list == tsd)
tsd_list = tsd->ts_next;
mutex_exit(&tsd_mutex);
kmem_free(tsd->ts_value, tsd->ts_nkeys * sizeof (void *));
kmem_free(tsd, sizeof (struct tsd_thread));
curthread->t_tsd = NULL;
}
static void *
tsd_realloc(void *old, size_t osize, size_t nsize)
{
void *new;
new = kmem_zalloc(nsize, KM_SLEEP);
if (old) {
bcopy(old, new, osize);
kmem_free(old, osize);
}
return (new);
}
int
servicing_interrupt()
{
int onintr = 0;
if (curthread->t_flag & T_INTR_THREAD)
return (1);
if (CPU_ON_INTR(CPU)) {
kpreempt_disable();
onintr = CPU_ON_INTR(CPU);
kpreempt_enable();
}
return (onintr);
}
void
thread_change_epri(kthread_t *t, pri_t disp_pri)
{
uint_t state;
ASSERT(THREAD_LOCK_HELD(t));
if (t->t_epri == disp_pri)
return;
state = t->t_state;
if ((state & (TS_SLEEP | TS_RUN | TS_WAIT)) == 0) {
t->t_epri = disp_pri;
if (state == TS_ONPROC) {
cpu_t *cp = t->t_disp_queue->disp_cpu;
if (t == cp->cpu_dispthread)
cp->cpu_dispatch_pri = DISP_PRIO(t);
}
} else if (state == TS_SLEEP) {
SOBJ_CHANGE_EPRI(t->t_sobj_ops, t, disp_pri);
} else if (state == TS_WAIT) {
if (disp_pri != t->t_epri)
waitq_change_pri(t, disp_pri);
} else {
(void) dispdeq(t);
t->t_epri = disp_pri;
setbackdq(t);
}
schedctl_set_cidpri(t);
}
int
thread_change_pri(kthread_t *t, pri_t disp_pri, int front)
{
uint_t state;
int on_rq = 0;
ASSERT(THREAD_LOCK_HELD(t));
state = t->t_state;
THREAD_WILLCHANGE_PRI(t, disp_pri);
if ((state & (TS_SLEEP | TS_RUN | TS_WAIT)) == 0) {
t->t_pri = disp_pri;
if (state == TS_ONPROC) {
cpu_t *cp = t->t_disp_queue->disp_cpu;
if (t == cp->cpu_dispthread)
cp->cpu_dispatch_pri = DISP_PRIO(t);
}
} else if (state == TS_SLEEP) {
if (disp_pri != t->t_pri)
SOBJ_CHANGE_PRI(t->t_sobj_ops, t, disp_pri);
} else if (state == TS_WAIT) {
if (disp_pri != t->t_pri)
waitq_change_pri(t, disp_pri);
} else {
on_rq = dispdeq(t);
ASSERT(on_rq);
t->t_pri = disp_pri;
if (front) {
setfrontdq(t);
} else {
setbackdq(t);
}
}
schedctl_set_cidpri(t);
return (on_rq);
}
static void
stkinfo_begin(kthread_t *t)
{
caddr_t start;
caddr_t end;
uint64_t *ptr;
if (t->t_stk > t->t_stkbase) {
start = t->t_stkbase;
end = t->t_stk;
} else {
start = t->t_stk;
end = t->t_stkbase;
}
if ((((uintptr_t)start) & 0x7) != 0) {
start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
}
end = (caddr_t)(((uintptr_t)end) & (~0x7));
if ((end <= start) || (end - start) > (1024 * 1024)) {
return;
}
ptr = (uint64_t *)((void *)start);
while (ptr < (uint64_t *)((void *)end)) {
*ptr++ = KMEM_STKINFO_PATTERN;
}
}
static void
stkinfo_end(kthread_t *t)
{
caddr_t start;
caddr_t end;
uint64_t *ptr;
size_t stksz;
size_t smallest = 0;
size_t percent = 0;
uint_t index = 0;
uint_t i;
static size_t smallest_percent = (size_t)-1;
static uint_t full = 0;
mutex_enter(&kmem_stkinfo_lock);
if (kmem_stkinfo_log == NULL) {
kmem_stkinfo_log = (kmem_stkinfo_t *)
kmem_zalloc(KMEM_STKINFO_LOG_SIZE *
(sizeof (kmem_stkinfo_t)), KM_NOSLEEP);
if (kmem_stkinfo_log == NULL) {
mutex_exit(&kmem_stkinfo_lock);
return;
}
}
mutex_exit(&kmem_stkinfo_lock);
if (t->t_stk > t->t_stkbase) {
start = t->t_stkbase;
end = t->t_stk;
} else {
start = t->t_stk;
end = t->t_stkbase;
}
stksz = end - start;
if ((((uintptr_t)start) & 0x7) != 0) {
start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
}
end = (caddr_t)(((uintptr_t)end) & (~0x7));
if ((end <= start) || (end - start) > (1024 * 1024)) {
return;
}
if (t->t_stk > t->t_stkbase) {
#if defined(__x86)
end -= (6 * sizeof (long));
#endif
ptr = (uint64_t *)((void *)start);
while (ptr < (uint64_t *)((void *)end)) {
if (*ptr != KMEM_STKINFO_PATTERN) {
percent = stkinfo_percent(end,
start, (caddr_t)ptr);
break;
}
ptr++;
}
} else {
ptr = (uint64_t *)((void *)end);
ptr--;
while (ptr >= (uint64_t *)((void *)start)) {
if (*ptr != KMEM_STKINFO_PATTERN) {
percent = stkinfo_percent(start,
end, (caddr_t)ptr);
break;
}
ptr--;
}
}
DTRACE_PROBE3(stack__usage, kthread_t *, t,
size_t, stksz, size_t, percent);
if (percent == 0) {
return;
}
mutex_enter(&kmem_stkinfo_lock);
if (full == KMEM_STKINFO_LOG_SIZE && percent < smallest_percent) {
mutex_exit(&kmem_stkinfo_lock);
return;
}
for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
if (kmem_stkinfo_log[i].percent == 0) {
index = i;
full++;
break;
}
if (smallest == 0) {
smallest = kmem_stkinfo_log[i].percent;
index = i;
continue;
}
if (kmem_stkinfo_log[i].percent < smallest) {
smallest = kmem_stkinfo_log[i].percent;
index = i;
}
}
if (percent >= kmem_stkinfo_log[index].percent) {
kmem_stkinfo_log[index].kthread = (caddr_t)t;
kmem_stkinfo_log[index].t_startpc = (caddr_t)t->t_startpc;
kmem_stkinfo_log[index].start = start;
kmem_stkinfo_log[index].stksz = stksz;
kmem_stkinfo_log[index].percent = percent;
kmem_stkinfo_log[index].t_tid = t->t_tid;
kmem_stkinfo_log[index].cmd[0] = '\0';
if (t->t_tid != 0) {
stksz = strlen((t->t_procp)->p_user.u_comm);
if (stksz >= KMEM_STKINFO_STR_SIZE) {
stksz = KMEM_STKINFO_STR_SIZE - 1;
kmem_stkinfo_log[index].cmd[stksz] = '\0';
} else {
stksz += 1;
}
(void) memcpy(kmem_stkinfo_log[index].cmd,
(t->t_procp)->p_user.u_comm, stksz);
}
if (percent < smallest_percent) {
smallest_percent = percent;
}
}
mutex_exit(&kmem_stkinfo_lock);
}
static size_t
stkinfo_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
{
size_t percent;
size_t s;
if (t_stk > t_stkbase) {
if (sp > t_stk) {
return (0);
}
if (sp < t_stkbase) {
return (100);
}
percent = t_stk - sp + 1;
s = t_stk - t_stkbase + 1;
} else {
if (sp < t_stk) {
return (0);
}
if (sp > t_stkbase) {
return (100);
}
percent = sp - t_stk + 1;
s = t_stkbase - t_stk + 1;
}
percent = ((100 * percent) / s) + 1;
if (percent > 100) {
percent = 100;
}
return (percent);
}
int
thread_setname(kthread_t *t, const char *name)
{
char *buf = NULL;
if (name != NULL && name[0] != '\0') {
for (size_t i = 0; name[i] != '\0'; i++) {
if (!isprint(name[i]))
return (EINVAL);
}
buf = kmem_zalloc(THREAD_NAME_MAX, KM_SLEEP);
(void) strlcpy(buf, name, THREAD_NAME_MAX);
}
mutex_enter(&ttoproc(t)->p_lock);
if (t->t_name == NULL) {
t->t_name = buf;
} else {
if (buf != NULL) {
(void) strlcpy(t->t_name, name, THREAD_NAME_MAX);
kmem_free(buf, THREAD_NAME_MAX);
} else {
bzero(t->t_name, THREAD_NAME_MAX);
}
}
mutex_exit(&ttoproc(t)->p_lock);
return (0);
}
int
thread_vsetname(kthread_t *t, const char *fmt, ...)
{
char name[THREAD_NAME_MAX];
va_list va;
int rc;
va_start(va, fmt);
rc = vsnprintf(name, sizeof (name), fmt, va);
va_end(va);
if (rc < 0)
return (EINVAL);
if (rc >= sizeof (name))
return (ENAMETOOLONG);
return (thread_setname(t, name));
}