#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/systm.h>
#include <sys/sysinfo.h>
#include <sys/var.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/inline.h>
#include <sys/disp.h>
#include <sys/class.h>
#include <sys/bitmap.h>
#include <sys/kmem.h>
#include <sys/cpuvar.h>
#include <sys/vtrace.h>
#include <sys/cpupart.h>
#include <sys/lgrp.h>
#include <sys/pg.h>
#include <sys/cmt.h>
#include <sys/bitset.h>
#include <sys/schedctl.h>
#include <sys/atomic.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
#include <sys/archsystm.h>
#include <sys/smt.h>
#include <vm/as.h>
#define BOUND_CPU 0x1
#define BOUND_PARTITION 0x2
#define BOUND_INTR 0x4
struct disp_queue_info {
disp_t *dp;
dispq_t *olddispq;
dispq_t *newdispq;
ulong_t *olddqactmap;
ulong_t *newdqactmap;
int oldnglobpris;
};
static void disp_dq_alloc(struct disp_queue_info *dptr, int numpris,
disp_t *dp);
static void disp_dq_assign(struct disp_queue_info *dptr, int numpris);
static void disp_dq_free(struct disp_queue_info *dptr);
static void generic_idle_cpu();
void (*idle_cpu)() = generic_idle_cpu;
static void idle_enter();
static void idle_exit();
static void generic_enq_thread(cpu_t *, int);
void (*disp_enq_thread)(cpu_t *, int) = generic_enq_thread;
pri_t kpreemptpri;
pri_t upreemptpri = 0;
pri_t intr_pri;
#define KPQPRI -1
pri_t kpqpri = KPQPRI;
disp_t cpu0_disp;
disp_lock_t swapped_lock;
int nswapped;
void disp_swapped_enq(kthread_t *tp);
static void disp_swapped_setrun(kthread_t *tp);
static void cpu_resched(cpu_t *cp, pri_t tpri);
int only_intr_kpreempt;
extern void set_idle_cpu(int cpun);
extern void unset_idle_cpu(int cpun);
static void setkpdq(kthread_t *tp, int borf);
#define SETKP_BACK 0
#define SETKP_FRONT 1
#define RECHOOSE_INTERVAL 3
int rechoose_interval = RECHOOSE_INTERVAL;
#define NOSTEAL_UNINITIALIZED (-1)
hrtime_t nosteal_nsec = NOSTEAL_UNINITIALIZED;
extern void cmp_set_nosteal_interval(void);
id_t defaultcid;
disp_lock_t transition_lock;
disp_lock_t stop_lock;
static void cpu_dispqalloc(int numpris);
#define T_DONTSTEAL (kthread_t *)(-1)
static kthread_t *disp_getwork(cpu_t *to);
static kthread_t *disp_getbest(disp_t *from);
static kthread_t *disp_ratify(kthread_t *tp, disp_t *kpq);
void swtch_to(kthread_t *);
static void
disp_setup(pri_t maxglobpri, pri_t oldnglobpris)
{
pri_t newnglobpris;
ASSERT(MUTEX_HELD(&cpu_lock));
newnglobpris = maxglobpri + 1 + LOCK_LEVEL;
if (newnglobpris > oldnglobpris) {
cpupart_kpqalloc(newnglobpris);
cpu_dispqalloc(newnglobpris);
intr_pri = maxglobpri;
if (only_intr_kpreempt) {
kpreemptpri = intr_pri + 1;
if (kpqpri == KPQPRI)
kpqpri = kpreemptpri;
}
v.v_nglobpris = newnglobpris;
}
}
void
dispinit(void)
{
id_t cid;
pri_t maxglobpri;
pri_t cl_maxglobpri;
maxglobpri = -1;
DISP_LOCK_INIT(&transition_lock);
disp_lock_enter_high(&transition_lock);
DISP_LOCK_INIT(&stop_lock);
mutex_enter(&cpu_lock);
CPU->cpu_disp->disp_maxrunpri = -1;
CPU->cpu_disp->disp_max_unbound_pri = -1;
cpupart_initialize_default();
for (cid = 0; cid < nclass; cid++) {
sclass_t *sc;
sc = &sclass[cid];
if (SCHED_INSTALLED(sc)) {
cl_maxglobpri = sc->cl_init(cid, PC_CLPARMSZ,
&sc->cl_funcs);
if (cl_maxglobpri > maxglobpri)
maxglobpri = cl_maxglobpri;
}
}
kpreemptpri = (pri_t)v.v_maxsyspri;
if (kpqpri == KPQPRI)
kpqpri = kpreemptpri;
ASSERT(maxglobpri >= 0);
disp_setup(maxglobpri, 0);
mutex_exit(&cpu_lock);
if (nosteal_nsec == NOSTEAL_UNINITIALIZED)
cmp_set_nosteal_interval();
if (getcid(defaultclass, &defaultcid) != 0) {
cmn_err(CE_PANIC, "Couldn't load default scheduling class '%s'",
defaultclass);
}
}
void
disp_add(sclass_t *clp)
{
pri_t maxglobpri;
pri_t cl_maxglobpri;
mutex_enter(&cpu_lock);
maxglobpri = (pri_t)(v.v_nglobpris - LOCK_LEVEL - 1);
cl_maxglobpri = clp->cl_init(clp - sclass, PC_CLPARMSZ, &clp->cl_funcs);
if (cl_maxglobpri > maxglobpri)
maxglobpri = cl_maxglobpri;
disp_setup(maxglobpri, v.v_nglobpris);
mutex_exit(&cpu_lock);
}
static void
cpu_dispqalloc(int numpris)
{
cpu_t *cpup;
struct disp_queue_info *disp_mem;
int i, num;
ASSERT(MUTEX_HELD(&cpu_lock));
disp_mem = kmem_zalloc(NCPU *
sizeof (struct disp_queue_info), KM_SLEEP);
i = 0;
cpup = cpu_list;
do {
disp_dq_alloc(&disp_mem[i], numpris, cpup->cpu_disp);
i++;
cpup = cpup->cpu_next;
} while (cpup != cpu_list);
num = i;
pause_cpus(NULL, NULL);
for (i = 0; i < num; i++)
disp_dq_assign(&disp_mem[i], numpris);
start_cpus();
for (i = 0; i < num; i++)
disp_dq_free(&disp_mem[i]);
kmem_free(disp_mem, NCPU * sizeof (struct disp_queue_info));
}
static void
disp_dq_alloc(struct disp_queue_info *dptr, int numpris, disp_t *dp)
{
dptr->newdispq = kmem_zalloc(numpris * sizeof (dispq_t), KM_SLEEP);
dptr->newdqactmap = kmem_zalloc(((numpris / BT_NBIPUL) + 1) *
sizeof (long), KM_SLEEP);
dptr->dp = dp;
}
static void
disp_dq_assign(struct disp_queue_info *dptr, int numpris)
{
disp_t *dp;
dp = dptr->dp;
dptr->olddispq = dp->disp_q;
dptr->olddqactmap = dp->disp_qactmap;
dptr->oldnglobpris = dp->disp_npri;
ASSERT(dptr->oldnglobpris < numpris);
if (dptr->olddispq != NULL) {
(void) kcopy(dptr->olddispq, dptr->newdispq,
dptr->oldnglobpris * sizeof (dispq_t));
(void) kcopy(dptr->olddqactmap, dptr->newdqactmap,
((dptr->oldnglobpris / BT_NBIPUL) + 1) *
sizeof (long));
}
dp->disp_q = dptr->newdispq;
dp->disp_qactmap = dptr->newdqactmap;
dp->disp_q_limit = &dptr->newdispq[numpris];
dp->disp_npri = numpris;
}
static void
disp_dq_free(struct disp_queue_info *dptr)
{
if (dptr->olddispq != NULL)
kmem_free(dptr->olddispq,
dptr->oldnglobpris * sizeof (dispq_t));
if (dptr->olddqactmap != NULL)
kmem_free(dptr->olddqactmap,
((dptr->oldnglobpris / BT_NBIPUL) + 1) * sizeof (long));
}
void
disp_cpu_init(cpu_t *cp)
{
disp_t *dp;
dispq_t *newdispq;
ulong_t *newdqactmap;
ASSERT(MUTEX_HELD(&cpu_lock));
if (cp == cpu0_disp.disp_cpu)
dp = &cpu0_disp;
else
dp = kmem_alloc(sizeof (disp_t), KM_SLEEP);
bzero(dp, sizeof (disp_t));
cp->cpu_disp = dp;
dp->disp_cpu = cp;
dp->disp_maxrunpri = -1;
dp->disp_max_unbound_pri = -1;
DISP_LOCK_INIT(&cp->cpu_thread_lock);
newdispq = kmem_zalloc(v.v_nglobpris * sizeof (dispq_t), KM_SLEEP);
newdqactmap = kmem_zalloc(((v.v_nglobpris / BT_NBIPUL) + 1) *
sizeof (long), KM_SLEEP);
dp->disp_q = newdispq;
dp->disp_qactmap = newdqactmap;
dp->disp_q_limit = &newdispq[v.v_nglobpris];
dp->disp_npri = v.v_nglobpris;
}
void
disp_cpu_fini(cpu_t *cp)
{
ASSERT(MUTEX_HELD(&cpu_lock));
disp_kp_free(cp->cpu_disp);
if (cp->cpu_disp != &cpu0_disp)
kmem_free(cp->cpu_disp, sizeof (disp_t));
}
void
disp_kp_alloc(disp_t *dq, pri_t npri)
{
struct disp_queue_info mem_info;
if (npri > dq->disp_npri) {
disp_dq_alloc(&mem_info, npri, dq);
disp_dq_assign(&mem_info, npri);
disp_dq_free(&mem_info);
}
}
void
disp_kp_free(disp_t *dq)
{
struct disp_queue_info mem_info;
mem_info.olddispq = dq->disp_q;
mem_info.olddqactmap = dq->disp_qactmap;
mem_info.oldnglobpris = dq->disp_npri;
disp_dq_free(&mem_info);
}
int
disp_anywork(void)
{
cpu_t *cp = CPU;
cpu_t *ocp;
volatile int *local_nrunnable = &cp->cpu_disp->disp_nrunnable;
if (!(cp->cpu_flags & CPU_OFFLINE)) {
if (CP_MAXRUNPRI(cp->cpu_part) >= 0)
return (1);
for (ocp = cp->cpu_next_part; ocp != cp;
ocp = ocp->cpu_next_part) {
ASSERT(CPU_ACTIVE(ocp));
if (*local_nrunnable > 0)
return (1);
if (ocp->cpu_dispatch_pri == -1 &&
(ocp->cpu_disp_flags & CPU_DISP_HALTED) == 0)
return (0);
if (ocp->cpu_disp->disp_max_unbound_pri != -1 &&
!((ocp->cpu_disp_flags & CPU_DISP_DONTSTEAL) &&
ocp->cpu_disp->disp_nrunnable == 1) &&
ocp->cpu_dispatch_pri != -1)
return (1);
}
}
return (0);
}
static void
idle_enter()
{
cpu_t *cp = CPU;
new_cpu_mstate(CMS_IDLE, gethrtime_unscaled());
CPU_STATS_ADDQ(cp, sys, idlethread, 1);
set_idle_cpu(cp->cpu_id);
}
static void
idle_exit()
{
cpu_t *cp = CPU;
new_cpu_mstate(CMS_SYSTEM, gethrtime_unscaled());
unset_idle_cpu(cp->cpu_id);
}
void
idle()
{
struct cpu *cp = CPU;
kthread_t *t;
idle_enter();
while (ncpus == 1) {
if (cp->cpu_disp->disp_nrunnable == 0) {
(*idle_cpu)();
continue;
}
idle_exit();
swtch();
idle_enter();
}
for (;;) {
while (cp->cpu_flags & CPU_QUIESCED)
(*idle_cpu)();
if (cp->cpu_disp->disp_nrunnable != 0) {
idle_exit();
swtch();
} else {
if (cp->cpu_flags & CPU_OFFLINE)
continue;
if ((t = disp_getwork(cp)) == NULL) {
if (cp->cpu_chosen_level != -1) {
disp_t *dp = cp->cpu_disp;
disp_t *kpq;
disp_lock_enter(&dp->disp_lock);
kpq = &cp->cpu_part->cp_kp_queue;
if (kpq->disp_maxrunpri == -1)
cp->cpu_chosen_level = -1;
disp_lock_exit(&dp->disp_lock);
}
(*idle_cpu)();
continue;
}
if (t == T_DONTSTEAL)
continue;
idle_exit();
swtch_to(t);
}
idle_enter();
}
}
void
preempt()
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(curthread);
if (panicstr)
return;
TRACE_0(TR_FAC_DISP, TR_PREEMPT_START, "preempt_start");
thread_lock(t);
if (t->t_state != TS_ONPROC || t->t_disp_queue != CPU->cpu_disp) {
CPU->cpu_kprunrun = 0;
thread_unlock_nopreempt(t);
TRACE_0(TR_FAC_DISP, TR_PREEMPT_END, "preempt_end");
} else {
if (lwp != NULL)
lwp->lwp_ru.nivcsw++;
CPU_STATS_ADDQ(CPU, sys, inv_swtch, 1);
THREAD_TRANSITION(t);
CL_PREEMPT(t);
DTRACE_SCHED(preempt);
thread_unlock_nopreempt(t);
TRACE_0(TR_FAC_DISP, TR_PREEMPT_END, "preempt_end");
swtch();
}
}
extern kthread_t *thread_unpin();
static kthread_t *
disp()
{
cpu_t *cpup;
disp_t *dp;
kthread_t *tp;
dispq_t *dq;
int maxrunword;
pri_t pri;
disp_t *kpq;
TRACE_0(TR_FAC_DISP, TR_DISP_START, "disp_start");
cpup = CPU;
dp = cpup->cpu_disp;
reschedule:
kpq = &cpup->cpu_part->cp_kp_queue;
while ((pri = kpq->disp_maxrunpri) >= 0 &&
pri >= dp->disp_maxrunpri &&
(cpup->cpu_flags & CPU_OFFLINE) == 0 &&
(tp = disp_getbest(kpq)) != NULL) {
if (disp_ratify(tp, kpq) != NULL) {
TRACE_1(TR_FAC_DISP, TR_DISP_END,
"disp_end:tid %p", tp);
return (tp);
}
}
disp_lock_enter(&dp->disp_lock);
pri = dp->disp_maxrunpri;
if (pri == -1) {
if (!(cpup->cpu_flags & CPU_OFFLINE)) {
disp_lock_exit(&dp->disp_lock);
if ((tp = disp_getwork(cpup)) == NULL ||
tp == T_DONTSTEAL) {
tp = cpup->cpu_idle_thread;
(void) splhigh();
THREAD_ONPROC(tp, cpup);
cpup->cpu_dispthread = tp;
cpup->cpu_dispatch_pri = -1;
cpup->cpu_runrun = cpup->cpu_kprunrun = 0;
cpup->cpu_chosen_level = -1;
}
} else {
disp_lock_exit_high(&dp->disp_lock);
tp = cpup->cpu_idle_thread;
THREAD_ONPROC(tp, cpup);
cpup->cpu_dispthread = tp;
cpup->cpu_dispatch_pri = -1;
cpup->cpu_runrun = cpup->cpu_kprunrun = 0;
cpup->cpu_chosen_level = -1;
}
TRACE_1(TR_FAC_DISP, TR_DISP_END,
"disp_end:tid %p", tp);
return (tp);
}
dq = &dp->disp_q[pri];
tp = dq->dq_first;
ASSERT(tp != NULL);
ASSERT(tp->t_schedflag & TS_LOAD);
DTRACE_SCHED2(dequeue, kthread_t *, tp, disp_t *, dp);
dp->disp_nrunnable--;
dq->dq_sruncnt--;
if ((dq->dq_first = tp->t_link) == NULL) {
ulong_t *dqactmap = dp->disp_qactmap;
ASSERT(dq->dq_sruncnt == 0);
dq->dq_last = NULL;
maxrunword = pri >> BT_ULSHIFT;
dqactmap[maxrunword] &= ~BT_BIW(pri);
if (dp->disp_nrunnable == 0) {
dp->disp_max_unbound_pri = -1;
dp->disp_maxrunpri = -1;
} else {
int ipri;
ipri = bt_gethighbit(dqactmap, maxrunword);
dp->disp_maxrunpri = ipri;
if (ipri < dp->disp_max_unbound_pri)
dp->disp_max_unbound_pri = ipri;
}
} else {
tp->t_link = NULL;
}
tp->t_schedflag |= TS_DONT_SWAP;
cpup->cpu_dispthread = tp;
cpup->cpu_dispatch_pri = pri;
ASSERT(pri == DISP_PRIO(tp));
thread_onproc(tp, cpup);
disp_lock_exit_high(&dp->disp_lock);
ASSERT(tp != NULL);
TRACE_1(TR_FAC_DISP, TR_DISP_END,
"disp_end:tid %p", tp);
if (disp_ratify(tp, kpq) == NULL)
goto reschedule;
return (tp);
}
void
swtch()
{
kthread_t *t = curthread;
kthread_t *next;
cpu_t *cp;
TRACE_0(TR_FAC_DISP, TR_SWTCH_START, "swtch_start");
if (t->t_flag & T_INTR_THREAD)
cpu_intr_swtch_enter(t);
if (t->t_intr != NULL) {
(void) splhigh();
cp = CPU;
ASSERT(CPU_ON_INTR(cp) == 0);
CPU_STATS_ADDQ(cp, sys, pswitch, 1);
CPU_STATS_ADDQ(cp, sys, intrblk, 1);
next = thread_unpin();
TRACE_0(TR_FAC_DISP, TR_RESUME_START, "resume_start");
resume_from_intr(next);
} else {
#ifdef DEBUG
if (t->t_state == TS_ONPROC &&
t->t_disp_queue->disp_cpu == CPU &&
t->t_preempt == 0) {
thread_lock(t);
ASSERT(t->t_state != TS_ONPROC ||
t->t_disp_queue->disp_cpu != CPU ||
t->t_preempt != 0);
thread_unlock_nopreempt(t);
}
#endif
cp = CPU;
next = disp();
ASSERT(CPU_ON_INTR(cp) == 0);
cp->cpu_disp_flags &= ~CPU_DISP_DONTSTEAL;
if (next != t) {
hrtime_t now;
now = gethrtime_unscaled();
pg_ev_thread_swtch(cp, now, t, next);
if ((t->t_state == TS_RUN) && (t->t_waitrq == 0)) {
t->t_waitrq = now;
}
restore_mstate(next);
CPU_STATS_ADDQ(cp, sys, pswitch, 1);
cp->cpu_last_swtch = t->t_disp_time = ddi_get_lbolt();
TRACE_0(TR_FAC_DISP, TR_RESUME_START, "resume_start");
if (dtrace_vtime_active)
dtrace_vtime_switch(next);
resume(next);
} else {
if (t->t_flag & T_INTR_THREAD)
cpu_intr_swtch_exit(t);
if (t->t_waitrq != 0)
t->t_waitrq = 0;
pg_ev_thread_remain(cp, t);
DTRACE_SCHED(remain__cpu);
TRACE_0(TR_FAC_DISP, TR_SWTCH_END, "swtch_end");
(void) spl0();
}
}
}
void
swtch_from_zombie()
{
kthread_t *next;
cpu_t *cpu = CPU;
TRACE_0(TR_FAC_DISP, TR_SWTCH_START, "swtch_start");
ASSERT(curthread->t_state == TS_ZOMB);
next = disp();
ASSERT(CPU_ON_INTR(CPU) == 0);
CPU_STATS_ADDQ(CPU, sys, pswitch, 1);
ASSERT(next != curthread);
TRACE_0(TR_FAC_DISP, TR_RESUME_START, "resume_start");
pg_ev_thread_swtch(cpu, gethrtime_unscaled(), curthread, next);
restore_mstate(next);
if (dtrace_vtime_active)
dtrace_vtime_switch(next);
resume_from_zombie(next);
}
#if defined(DEBUG) && (defined(DISP_DEBUG) || defined(lint))
static int
search_disp_queues(disp_t *dp, kthread_t *tp)
{
dispq_t *dq;
dispq_t *eq;
disp_lock_enter_high(&dp->disp_lock);
for (dq = dp->disp_q, eq = dp->disp_q_limit; dq < eq; ++dq) {
kthread_t *rp;
ASSERT(dq->dq_last == NULL || dq->dq_last->t_link == NULL);
for (rp = dq->dq_first; rp; rp = rp->t_link)
if (tp == rp) {
disp_lock_exit_high(&dp->disp_lock);
return (1);
}
}
disp_lock_exit_high(&dp->disp_lock);
return (0);
}
static int
thread_on_queue(kthread_t *tp)
{
cpu_t *cp;
struct cpupart *part;
ASSERT(getpil() >= DISP_LEVEL);
cp = CPU;
do {
if (search_disp_queues(cp->cpu_disp, tp))
return (1);
} while ((cp = cp->cpu_next_onln) != CPU);
part = CPU->cpu_part;
do {
if (search_disp_queues(&part->cp_kp_queue, tp))
return (1);
} while ((part = part->cp_next) != CPU->cpu_part);
return (0);
}
#else
#define thread_on_queue(tp) 0
#endif
void
swtch_to(kthread_t *next)
{
cpu_t *cp = CPU;
hrtime_t now;
TRACE_0(TR_FAC_DISP, TR_SWTCH_START, "swtch_start");
CPU_STATS_ADDQ(cp, sys, pswitch, 1);
TRACE_0(TR_FAC_DISP, TR_RESUME_START, "resume_start");
now = gethrtime_unscaled();
pg_ev_thread_swtch(cp, now, curthread, next);
cp->cpu_disp_flags &= ~CPU_DISP_DONTSTEAL;
cp->cpu_last_swtch = curthread->t_disp_time = ddi_get_lbolt();
if ((curthread->t_state == TS_RUN) && (curthread->t_waitrq == 0)) {
curthread->t_waitrq = now;
}
restore_mstate(next);
if (dtrace_vtime_active)
dtrace_vtime_switch(next);
resume(next);
}
static void
cpu_resched(cpu_t *cp, pri_t tpri)
{
int call_poke_cpu = 0;
pri_t cpupri = cp->cpu_dispatch_pri;
if (cpupri != CPU_IDLE_PRI && cpupri < tpri) {
TRACE_2(TR_FAC_DISP, TR_CPU_RESCHED,
"CPU_RESCHED:Tpri %d Cpupri %d", tpri, cpupri);
if (tpri >= upreemptpri && cp->cpu_runrun == 0) {
cp->cpu_runrun = 1;
aston(cp->cpu_dispthread);
if (tpri < kpreemptpri && cp != CPU)
call_poke_cpu = 1;
}
if (tpri >= kpreemptpri && cp->cpu_kprunrun == 0) {
cp->cpu_kprunrun = 1;
if (cp != CPU)
call_poke_cpu = 1;
}
}
membar_enter();
if (call_poke_cpu)
poke_cpu(cp->cpu_id);
}
#define RUNQ_MATCH_PRI 16
#define RUNQ_MAX_DIFF 2
#define RUNQ_LEN(cp, pri) ((cp)->cpu_disp->disp_q[pri].dq_sruncnt)
#define THREAD_HAS_CACHE_WARMTH(thread) \
((thread == curthread) || \
((ddi_get_lbolt() - thread->t_disp_time) <= rechoose_interval))
void
setbackdq(kthread_t *tp)
{
dispq_t *dq;
disp_t *dp;
cpu_t *cp;
pri_t tpri;
int bound;
boolean_t self;
ASSERT(THREAD_LOCK_HELD(tp));
ASSERT((tp->t_schedflag & TS_ALLSTART) == 0);
ASSERT(!thread_on_queue(tp));
if ((tp->t_schedflag & (TS_LOAD | TS_ON_SWAPQ)) != TS_LOAD) {
disp_swapped_setrun(tp);
return;
}
self = (tp == curthread);
if (tp->t_bound_cpu || tp->t_weakbound_cpu)
bound = 1;
else
bound = 0;
tpri = DISP_PRIO(tp);
if (ncpus == 1)
cp = tp->t_cpu;
else if (!bound) {
if (tpri >= kpqpri) {
setkpdq(tp, SETKP_BACK);
return;
}
if ((!THREAD_HAS_CACHE_WARMTH(tp)) ||
!smt_should_run(tp, tp->t_cpu) ||
(tp->t_cpu == cpu_inmotion) ||
!LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, tp->t_cpu)) {
cp = disp_lowpri_cpu(tp->t_cpu, tp, tpri);
} else {
cp = tp->t_cpu;
}
if (tp->t_cpupart == cp->cpu_part) {
int qlen;
cp = cmt_balance(tp, cp);
qlen = RUNQ_LEN(cp, tpri);
if (tpri >= RUNQ_MATCH_PRI &&
!(tp->t_schedflag & TS_RUNQMATCH))
qlen -= RUNQ_MAX_DIFF;
if (qlen > 0) {
cpu_t *newcp;
if (tp->t_lpl->lpl_lgrpid == LGRP_ROOTID) {
newcp = cp->cpu_next_part;
} else if ((newcp = cp->cpu_next_lpl) == cp) {
newcp = cp->cpu_next_part;
}
if (smt_should_run(tp, newcp) &&
RUNQ_LEN(newcp, tpri) < qlen) {
DTRACE_PROBE3(runq__balance,
kthread_t *, tp,
cpu_t *, cp, cpu_t *, newcp);
cp = newcp;
}
}
} else {
cp = disp_lowpri_cpu(tp->t_cpupart->cp_cpulist, tp,
tp->t_pri);
}
ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
} else {
cp = tp->t_weakbound_cpu ?
tp->t_weakbound_cpu : tp->t_bound_cpu;
}
if ((!self) && (tp->t_waitrq == 0)) {
hrtime_t curtime;
curtime = gethrtime_unscaled();
(void) cpu_update_pct(tp, curtime);
tp->t_waitrq = curtime;
} else {
(void) cpu_update_pct(tp, gethrtime_unscaled());
}
dp = cp->cpu_disp;
disp_lock_enter_high(&dp->disp_lock);
DTRACE_SCHED3(enqueue, kthread_t *, tp, disp_t *, dp, int, 0);
TRACE_3(TR_FAC_DISP, TR_BACKQ, "setbackdq:pri %d cpu %p tid %p",
tpri, cp, tp);
ASSERT(tpri >= 0 && tpri < dp->disp_npri);
THREAD_RUN(tp, &dp->disp_lock);
tp->t_disp_queue = dp;
tp->t_link = NULL;
dq = &dp->disp_q[tpri];
dp->disp_nrunnable++;
if (!bound)
dp->disp_steal = 0;
membar_enter();
if (dq->dq_sruncnt++ != 0) {
ASSERT(dq->dq_first != NULL);
dq->dq_last->t_link = tp;
dq->dq_last = tp;
} else {
ASSERT(dq->dq_first == NULL);
ASSERT(dq->dq_last == NULL);
dq->dq_first = dq->dq_last = tp;
BT_SET(dp->disp_qactmap, tpri);
if (tpri > dp->disp_maxrunpri) {
dp->disp_maxrunpri = tpri;
membar_enter();
cpu_resched(cp, tpri);
}
}
if (!bound && tpri > dp->disp_max_unbound_pri) {
if (self && dp->disp_max_unbound_pri == -1 && cp == CPU) {
cp->cpu_disp_flags |= CPU_DISP_DONTSTEAL;
}
dp->disp_max_unbound_pri = tpri;
}
(*disp_enq_thread)(cp, bound);
}
void
setfrontdq(kthread_t *tp)
{
disp_t *dp;
dispq_t *dq;
cpu_t *cp;
pri_t tpri;
int bound;
ASSERT(THREAD_LOCK_HELD(tp));
ASSERT((tp->t_schedflag & TS_ALLSTART) == 0);
ASSERT(!thread_on_queue(tp));
if ((tp->t_schedflag & (TS_LOAD | TS_ON_SWAPQ)) != TS_LOAD) {
disp_swapped_setrun(tp);
return;
}
if (tp->t_bound_cpu || tp->t_weakbound_cpu)
bound = 1;
else
bound = 0;
tpri = DISP_PRIO(tp);
if (ncpus == 1)
cp = tp->t_cpu;
else if (!bound) {
if (tpri >= kpqpri) {
setkpdq(tp, SETKP_FRONT);
return;
}
cp = tp->t_cpu;
if (tp->t_cpupart == cp->cpu_part) {
if (!LGRP_CONTAINS_CPU(tp->t_lpl->lpl_lgrp, cp) ||
cp == cpu_inmotion ||
(tpri < cp->cpu_disp->disp_maxrunpri &&
!THREAD_HAS_CACHE_WARMTH(tp))) {
cp = disp_lowpri_cpu(tp->t_cpu, tp, tpri);
}
} else {
cp = disp_lowpri_cpu(tp->t_cpupart->cp_cpulist,
tp, tp->t_pri);
}
ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
} else {
cp = tp->t_weakbound_cpu ?
tp->t_weakbound_cpu : tp->t_bound_cpu;
}
if ((tp != curthread) && (tp->t_waitrq == 0)) {
hrtime_t curtime;
curtime = gethrtime_unscaled();
(void) cpu_update_pct(tp, curtime);
tp->t_waitrq = curtime;
} else {
(void) cpu_update_pct(tp, gethrtime_unscaled());
}
dp = cp->cpu_disp;
disp_lock_enter_high(&dp->disp_lock);
TRACE_2(TR_FAC_DISP, TR_FRONTQ, "frontq:pri %d tid %p", tpri, tp);
DTRACE_SCHED3(enqueue, kthread_t *, tp, disp_t *, dp, int, 1);
ASSERT(tpri >= 0 && tpri < dp->disp_npri);
THREAD_RUN(tp, &dp->disp_lock);
tp->t_disp_queue = dp;
dq = &dp->disp_q[tpri];
dp->disp_nrunnable++;
if (!bound)
dp->disp_steal = 0;
membar_enter();
if (dq->dq_sruncnt++ != 0) {
ASSERT(dq->dq_last != NULL);
tp->t_link = dq->dq_first;
dq->dq_first = tp;
} else {
ASSERT(dq->dq_last == NULL);
ASSERT(dq->dq_first == NULL);
tp->t_link = NULL;
dq->dq_first = dq->dq_last = tp;
BT_SET(dp->disp_qactmap, tpri);
if (tpri > dp->disp_maxrunpri) {
dp->disp_maxrunpri = tpri;
membar_enter();
cpu_resched(cp, tpri);
}
}
if (!bound && tpri > dp->disp_max_unbound_pri) {
if (tp == curthread && dp->disp_max_unbound_pri == -1 &&
cp == CPU) {
cp->cpu_disp_flags |= CPU_DISP_DONTSTEAL;
}
dp->disp_max_unbound_pri = tpri;
}
(*disp_enq_thread)(cp, bound);
}
static void
setkpdq(kthread_t *tp, int borf)
{
dispq_t *dq;
disp_t *dp;
cpu_t *cp;
pri_t tpri;
tpri = DISP_PRIO(tp);
dp = &tp->t_cpupart->cp_kp_queue;
disp_lock_enter_high(&dp->disp_lock);
TRACE_2(TR_FAC_DISP, TR_FRONTQ, "frontq:pri %d tid %p", tpri, tp);
ASSERT(tpri >= 0 && tpri < dp->disp_npri);
DTRACE_SCHED3(enqueue, kthread_t *, tp, disp_t *, dp, int, borf);
THREAD_RUN(tp, &dp->disp_lock);
tp->t_disp_queue = dp;
dp->disp_nrunnable++;
dq = &dp->disp_q[tpri];
if (dq->dq_sruncnt++ != 0) {
if (borf == SETKP_BACK) {
ASSERT(dq->dq_first != NULL);
tp->t_link = NULL;
dq->dq_last->t_link = tp;
dq->dq_last = tp;
} else {
ASSERT(dq->dq_last != NULL);
tp->t_link = dq->dq_first;
dq->dq_first = tp;
}
} else {
if (borf == SETKP_BACK) {
ASSERT(dq->dq_first == NULL);
ASSERT(dq->dq_last == NULL);
dq->dq_first = dq->dq_last = tp;
} else {
ASSERT(dq->dq_last == NULL);
ASSERT(dq->dq_first == NULL);
tp->t_link = NULL;
dq->dq_first = dq->dq_last = tp;
}
BT_SET(dp->disp_qactmap, tpri);
if (tpri > dp->disp_max_unbound_pri)
dp->disp_max_unbound_pri = tpri;
if (tpri > dp->disp_maxrunpri) {
dp->disp_maxrunpri = tpri;
membar_enter();
}
}
cp = tp->t_cpu;
if (tp->t_cpupart != cp->cpu_part) {
cp = tp->t_cpupart->cp_cpulist;
}
cp = disp_lowpri_cpu(cp, tp, tp->t_pri);
disp_lock_enter_high(&cp->cpu_disp->disp_lock);
ASSERT((cp->cpu_flags & CPU_QUIESCED) == 0);
if (cp->cpu_chosen_level < tpri)
cp->cpu_chosen_level = tpri;
cpu_resched(cp, tpri);
disp_lock_exit_high(&cp->cpu_disp->disp_lock);
(*disp_enq_thread)(cp, 0);
}
int
dispdeq(kthread_t *tp)
{
disp_t *dp;
dispq_t *dq;
kthread_t *rp;
kthread_t *trp;
kthread_t **ptp;
int tpri;
ASSERT(THREAD_LOCK_HELD(tp));
if (tp->t_state != TS_RUN)
return (0);
if ((tp->t_schedflag & (TS_LOAD | TS_ON_SWAPQ)) != TS_LOAD)
return (1);
tpri = DISP_PRIO(tp);
dp = tp->t_disp_queue;
ASSERT(tpri < dp->disp_npri);
dq = &dp->disp_q[tpri];
ptp = &dq->dq_first;
rp = *ptp;
trp = NULL;
ASSERT(dq->dq_last == NULL || dq->dq_last->t_link == NULL);
while (rp != tp && rp != NULL) {
trp = rp;
ptp = &trp->t_link;
rp = trp->t_link;
}
if (rp == NULL) {
panic("dispdeq: thread not on queue");
}
DTRACE_SCHED2(dequeue, kthread_t *, tp, disp_t *, dp);
if ((*ptp = rp->t_link) == NULL)
dq->dq_last = trp;
dp->disp_nrunnable--;
if (--dq->dq_sruncnt == 0) {
dp->disp_qactmap[tpri >> BT_ULSHIFT] &= ~BT_BIW(tpri);
if (dp->disp_nrunnable == 0) {
dp->disp_max_unbound_pri = -1;
dp->disp_maxrunpri = -1;
} else if (tpri == dp->disp_maxrunpri) {
int ipri;
ipri = bt_gethighbit(dp->disp_qactmap,
dp->disp_maxrunpri >> BT_ULSHIFT);
if (ipri < dp->disp_max_unbound_pri)
dp->disp_max_unbound_pri = ipri;
dp->disp_maxrunpri = ipri;
}
}
tp->t_link = NULL;
THREAD_TRANSITION(tp);
return (1);
}
void
dq_sruninc(kthread_t *t)
{
ASSERT(t->t_state == TS_RUN);
ASSERT(t->t_schedflag & TS_LOAD);
THREAD_TRANSITION(t);
setfrontdq(t);
}
void
dq_srundec(kthread_t *t)
{
ASSERT(t->t_schedflag & TS_LOAD);
(void) dispdeq(t);
disp_swapped_enq(t);
}
void
disp_swapped_enq(kthread_t *tp)
{
ASSERT(THREAD_LOCK_HELD(tp));
ASSERT(tp->t_schedflag & TS_LOAD);
switch (tp->t_state) {
case TS_RUN:
disp_lock_enter_high(&swapped_lock);
THREAD_SWAP(tp, &swapped_lock);
break;
case TS_ONPROC:
disp_lock_enter_high(&swapped_lock);
THREAD_TRANSITION(tp);
wake_sched_sec = 1;
THREAD_SWAP(tp, &swapped_lock);
break;
default:
panic("disp_swapped: tp: %p bad t_state", (void *)tp);
}
}
static void
disp_swapped_setrun(kthread_t *tp)
{
ASSERT(THREAD_LOCK_HELD(tp));
ASSERT((tp->t_schedflag & (TS_LOAD | TS_ON_SWAPQ)) != TS_LOAD);
switch (tp->t_state) {
case TS_SLEEP:
disp_lock_enter_high(&swapped_lock);
if (DISP_PRIO(tp) > maxclsyspri)
wake_sched = 1;
else
wake_sched_sec = 1;
THREAD_RUN(tp, &swapped_lock);
break;
case TS_RUN:
break;
default:
panic("disp_swapped_setrun: tp: %p bad t_state", (void *)tp);
}
}
void
cpu_surrender(kthread_t *tp)
{
cpu_t *cpup;
int max_pri;
int max_run_pri;
klwp_t *lwp;
ASSERT(THREAD_LOCK_HELD(tp));
if (tp->t_state != TS_ONPROC)
return;
cpup = tp->t_disp_queue->disp_cpu;
max_pri = cpup->cpu_disp->disp_maxrunpri;
max_run_pri = CP_MAXRUNPRI(cpup->cpu_part);
if (max_pri < max_run_pri)
max_pri = max_run_pri;
if (tp->t_cid == sysdccid) {
uint_t t_pri = DISP_PRIO(tp);
if (t_pri > max_pri)
return;
cpup->cpu_runrun = cpup->cpu_kprunrun = 1;
} else {
cpup->cpu_runrun = 1;
if (max_pri >= kpreemptpri && cpup->cpu_kprunrun == 0) {
cpup->cpu_kprunrun = 1;
}
}
membar_enter();
DTRACE_SCHED1(surrender, kthread_t *, tp);
if (tp != curthread || (lwp = tp->t_lwp) == NULL ||
lwp->lwp_state != LWP_USER) {
aston(tp);
if (cpup != CPU)
poke_cpu(cpup->cpu_id);
}
TRACE_2(TR_FAC_DISP, TR_CPU_SURRENDER,
"cpu_surrender:tid %p cpu %p", tp, cpup);
}
static kthread_t *
disp_ratify(kthread_t *tp, disp_t *kpq)
{
pri_t tpri, maxpri;
pri_t maxkpri;
cpu_t *cpup;
ASSERT(tp != NULL);
cpup = CPU;
if (cpup->cpu_runrun != 0)
cpup->cpu_runrun = 0;
if (cpup->cpu_kprunrun != 0)
cpup->cpu_kprunrun = 0;
if (cpup->cpu_chosen_level != -1)
cpup->cpu_chosen_level = -1;
membar_enter();
tpri = DISP_PRIO(tp);
maxpri = cpup->cpu_disp->disp_maxrunpri;
maxkpri = kpq->disp_maxrunpri;
if (maxpri < maxkpri)
maxpri = maxkpri;
if (tpri < maxpri) {
cpup->cpu_dispthread = curthread;
cpup->cpu_dispatch_pri = DISP_PRIO(curthread);
thread_lock_high(tp);
THREAD_TRANSITION(tp);
setfrontdq(tp);
thread_unlock_nopreempt(tp);
tp = NULL;
}
return (tp);
}
static kthread_t *
disp_getwork(cpu_t *cp)
{
cpu_t *ocp;
cpu_t *ocp_start;
cpu_t *tcp;
kthread_t *tp;
kthread_t *retval = NULL;
pri_t maxpri;
disp_t *kpq;
lpl_t *lpl, *lpl_leaf;
int leafidx, startidx;
hrtime_t stealtime;
lgrp_id_t local_id;
maxpri = -1;
tcp = NULL;
kpq = &cp->cpu_part->cp_kp_queue;
while (kpq->disp_maxrunpri >= 0) {
tp = (disp_getbest(kpq));
if (tp)
return (disp_ratify(tp, kpq));
}
kpreempt_disable();
lpl = lpl_leaf = cp->cpu_lpl;
local_id = lpl_leaf->lpl_lgrpid;
leafidx = startidx = 0;
do {
do {
if (lpl_leaf != cp->cpu_lpl)
ocp = lpl_leaf->lpl_cpus;
else
ocp = cp->cpu_next_lpl;
ocp_start = ocp;
do {
pri_t pri;
ASSERT(CPU_ACTIVE(ocp));
if (cp->cpu_disp->disp_nrunnable != 0) {
kpreempt_enable();
return (NULL);
}
if (ocp->cpu_dispatch_pri == -1) {
if (ocp->cpu_disp_flags &
CPU_DISP_HALTED ||
ocp->cpu_intr_actv != 0)
continue;
else
goto next_level;
}
if ((ocp->cpu_disp_flags &
CPU_DISP_DONTSTEAL) &&
ocp->cpu_disp->disp_nrunnable == 1)
continue;
pri = ocp->cpu_disp->disp_max_unbound_pri;
if (pri > maxpri) {
stealtime = ocp->cpu_disp->disp_steal;
if (stealtime == 0 ||
stealtime - gethrtime() <= 0) {
maxpri = pri;
tcp = ocp;
} else {
retval = T_DONTSTEAL;
}
}
} while ((ocp = ocp->cpu_next_lpl) != ocp_start);
if ((lpl_leaf = lpl->lpl_rset[++leafidx]) == NULL) {
leafidx = 0;
lpl_leaf = lpl->lpl_rset[leafidx];
}
} while (leafidx != startidx);
next_level:
if ((lpl = lpl->lpl_parent) != NULL) {
leafidx = startidx = lpl->lpl_id2rset[local_id];
lpl_leaf = lpl->lpl_rset[leafidx];
}
} while (!tcp && lpl);
kpreempt_enable();
if (tcp && cp->cpu_disp->disp_nrunnable == 0) {
tp = disp_getbest(tcp->cpu_disp);
if (tp == NULL || tp == T_DONTSTEAL)
return (tp);
return (disp_ratify(tp, kpq));
}
return (retval);
}
static void
disp_fix_unbound_pri(disp_t *dp, pri_t pri)
{
kthread_t *tp;
dispq_t *dq;
ulong_t *dqactmap = dp->disp_qactmap;
ulong_t mapword;
int wx;
ASSERT(DISP_LOCK_HELD(&dp->disp_lock));
ASSERT(pri >= 0);
do {
wx = pri >> BT_ULSHIFT;
mapword = dqactmap[wx] & (BT_BIW(pri) - 1);
if (mapword != 0) {
pri = (wx << BT_ULSHIFT) + highbit(mapword) - 1;
} else if (wx > 0) {
pri = bt_gethighbit(dqactmap, wx - 1);
if (pri < 0)
break;
} else {
pri = -1;
break;
}
dq = &dp->disp_q[pri];
tp = dq->dq_first;
while (tp && (tp->t_bound_cpu || tp->t_weakbound_cpu)) {
tp = tp->t_link;
}
} while (tp == NULL);
if (dp->disp_max_unbound_pri != pri)
dp->disp_max_unbound_pri = pri;
}
void
disp_adjust_unbound_pri(kthread_t *tp)
{
disp_t *dp;
pri_t tpri;
ASSERT(THREAD_LOCK_HELD(tp));
if (tp->t_bound_cpu == NULL ||
tp->t_state != TS_RUN ||
tp->t_schedflag & TS_ON_SWAPQ)
return;
tpri = DISP_PRIO(tp);
dp = tp->t_bound_cpu->cpu_disp;
ASSERT(tpri >= 0 && tpri < dp->disp_npri);
if (tpri > dp->disp_max_unbound_pri)
dp->disp_max_unbound_pri = tpri;
}
static kthread_t *
disp_getbest(disp_t *dp)
{
kthread_t *tp;
dispq_t *dq;
pri_t pri;
cpu_t *cp, *tcp;
boolean_t allbound;
disp_lock_enter(&dp->disp_lock);
tcp = dp->disp_cpu;
cp = CPU;
pri = dp->disp_max_unbound_pri;
if (pri == -1 ||
(tcp != NULL && (tcp->cpu_disp_flags & CPU_DISP_DONTSTEAL) &&
tcp->cpu_disp->disp_nrunnable == 1)) {
disp_lock_exit_nopreempt(&dp->disp_lock);
return (NULL);
}
dq = &dp->disp_q[pri];
allbound = B_TRUE;
for (tp = dq->dq_first; tp != NULL; tp = tp->t_link) {
hrtime_t now, nosteal, rqtime;
if (tp->t_bound_cpu || tp->t_weakbound_cpu)
continue;
allbound = B_FALSE;
if (tcp == NULL ||
pri >= minclsyspri ||
tp->t_cpu != tcp)
break;
if (pg_cmt_can_migrate(cp, tcp))
break;
nosteal = nosteal_nsec;
if (nosteal == 0)
break;
now = gethrtime_unscaled();
rqtime = now - tp->t_waitrq;
scalehrtime(&rqtime);
if (rqtime > nosteal || rqtime < 0)
break;
DTRACE_PROBE4(nosteal, kthread_t *, tp,
cpu_t *, tcp, cpu_t *, cp, hrtime_t, rqtime);
scalehrtime(&now);
now += (nosteal - rqtime);
if (now < dp->disp_steal)
dp->disp_steal = now;
}
if (allbound)
disp_fix_unbound_pri(dp, pri);
if (tp == NULL) {
disp_lock_exit_nopreempt(&dp->disp_lock);
return (allbound ? NULL : T_DONTSTEAL);
}
#ifdef DEBUG
{
int thread_was_on_queue;
thread_was_on_queue = dispdeq(tp);
ASSERT(thread_was_on_queue);
}
#else
(void) dispdeq(tp);
#endif
dp->disp_steal = 0;
tp->t_schedflag |= TS_DONT_SWAP;
tp->t_disp_queue = cp->cpu_disp;
cp->cpu_dispthread = tp;
cp->cpu_dispatch_pri = pri;
disp_lock_enter_high(&cp->cpu_disp->disp_lock);
disp_lock_exit_high(&cp->cpu_disp->disp_lock);
ASSERT(pri == DISP_PRIO(tp));
DTRACE_PROBE3(steal, kthread_t *, tp, cpu_t *, tcp, cpu_t *, cp);
thread_onproc(tp, cp);
return (tp);
}
static int
disp_bound_common(cpu_t *cp, int threadlistsafe, int flag)
{
int found = 0;
kthread_t *tp;
ASSERT(flag);
if (!threadlistsafe)
mutex_enter(&pidlock);
tp = curthread;
do {
if (tp->t_state != TS_FREE) {
if ((tp->t_flag & T_INTR_THREAD) &&
!(flag & BOUND_INTR))
continue;
if (tp == cp->cpu_idle_thread)
continue;
if (tp == cp->cpu_pause_thread)
continue;
if ((flag & BOUND_CPU) &&
(tp->t_bound_cpu == cp ||
tp->t_bind_cpu == cp->cpu_id ||
tp->t_weakbound_cpu == cp)) {
found = 1;
break;
}
if ((flag & BOUND_PARTITION) &&
(tp->t_cpupart == cp->cpu_part)) {
found = 1;
break;
}
}
} while ((tp = tp->t_next) != curthread && found == 0);
if (!threadlistsafe)
mutex_exit(&pidlock);
return (found);
}
int
disp_bound_threads(cpu_t *cp, int threadlistsafe)
{
return (disp_bound_common(cp, threadlistsafe, BOUND_CPU));
}
int
disp_bound_anythreads(cpu_t *cp, int threadlistsafe)
{
return (disp_bound_common(cp, threadlistsafe, BOUND_CPU | BOUND_INTR));
}
int
disp_bound_partition(cpu_t *cp, int threadlistsafe)
{
return (disp_bound_common(cp, threadlistsafe, BOUND_PARTITION));
}
void
disp_cpu_inactive(cpu_t *cp)
{
kthread_t *tp;
disp_t *dp = cp->cpu_disp;
dispq_t *dq;
pri_t pri;
int wasonq;
disp_lock_enter(&dp->disp_lock);
while ((pri = dp->disp_max_unbound_pri) != -1) {
dq = &dp->disp_q[pri];
tp = dq->dq_first;
while (tp != NULL && tp->t_bound_cpu != NULL) {
tp = tp->t_link;
}
if (tp == NULL) {
disp_fix_unbound_pri(dp, pri);
continue;
}
wasonq = dispdeq(tp);
ASSERT(wasonq);
ASSERT(tp->t_weakbound_cpu == NULL);
setbackdq(tp);
ASSERT(tp->t_cpu != cp);
thread_unlock(tp);
disp_lock_enter(&dp->disp_lock);
}
disp_lock_exit(&dp->disp_lock);
}
static pri_t
cpu_score(cpu_t *cp, kthread_t *tp)
{
pri_t score;
if (tp == curthread && cp == curthread->t_cpu)
score = 2 * CPU_IDLE_PRI;
else if (cp == cpu_inmotion)
score = SHRT_MAX;
else
score = 2 * cp->cpu_dispatch_pri;
if (2 * cp->cpu_disp->disp_maxrunpri > score)
score = 2 * cp->cpu_disp->disp_maxrunpri;
if (2 * cp->cpu_chosen_level > score)
score = 2 * cp->cpu_chosen_level;
return (smt_adjust_cpu_score(tp, cp, score));
}
cpu_t *
disp_lowpri_cpu(cpu_t *hint, kthread_t *tp, pri_t tpri)
{
cpu_t *bestcpu;
cpu_t *besthomecpu;
cpu_t *cp, *cpstart;
klgrpset_t done;
lpl_t *lpl_iter, *lpl_leaf;
ASSERT(hint != NULL);
ASSERT(tp->t_lpl->lpl_ncpu > 0);
bestcpu = besthomecpu = NULL;
klgrpset_clear(done);
lpl_iter = tp->t_lpl;
do {
pri_t best = SHRT_MAX;
klgrpset_t cur_set;
klgrpset_clear(cur_set);
for (int i = 0; i < lpl_iter->lpl_nrset; i++) {
lpl_leaf = lpl_iter->lpl_rset[i];
if (klgrpset_ismember(done, lpl_leaf->lpl_lgrpid))
continue;
klgrpset_add(cur_set, lpl_leaf->lpl_lgrpid);
if (hint->cpu_lpl == lpl_leaf)
cp = cpstart = hint;
else
cp = cpstart = lpl_leaf->lpl_cpus;
do {
pri_t score = cpu_score(cp, tp);
if (score < best) {
best = score;
bestcpu = cp;
if (score / 2 == CPU_IDLE_PRI)
goto out;
}
} while ((cp = cp->cpu_next_lpl) != cpstart);
}
if (bestcpu != NULL && tpri > (best / 2))
goto out;
if (besthomecpu == NULL)
besthomecpu = bestcpu;
klgrpset_or(done, cur_set);
} while ((lpl_iter = lpl_iter->lpl_parent) != NULL);
bestcpu = besthomecpu;
out:
ASSERT((bestcpu->cpu_flags & CPU_QUIESCED) == 0);
return (bestcpu);
}
static void
generic_idle_cpu(void)
{
}
static void
generic_enq_thread(cpu_t *cpu, int bound)
{
}
cpu_t *
disp_choose_best_cpu(void)
{
kthread_t *t = curthread;
cpu_t *curcpu = CPU;
ASSERT(t->t_preempt > 0);
ASSERT(t->t_state == TS_ONPROC);
ASSERT(t->t_schedflag & TS_VCPU);
if (smt_should_run(t, curcpu))
return (curcpu);
return (disp_lowpri_cpu(curcpu, t, t->t_pri));
}