#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/rtprio.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
#include <sys/resourcevar.h>
#include <sys/spinlock.h>
#include <machine/cpu.h>
#include <machine/smp.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#define MAXPRI 128
#define PRIBASE_REALTIME 0
#define PRIBASE_NORMAL MAXPRI
#define PRIBASE_IDLE (MAXPRI * 2)
#define PRIBASE_THREAD (MAXPRI * 3)
#define PRIBASE_NULL (MAXPRI * 4)
#define lwp_priority lwp_usdata.bsd4.priority
#define lwp_estcpu lwp_usdata.bsd4.estcpu
static void dummy_acquire_curproc(struct lwp *lp);
static void dummy_release_curproc(struct lwp *lp);
static void dummy_select_curproc(globaldata_t gd);
static void dummy_setrunqueue(struct lwp *lp);
static void dummy_schedulerclock(struct lwp *lp, sysclock_t period,
sysclock_t cpstamp);
static void dummy_recalculate_estcpu(struct lwp *lp);
static void dummy_resetpriority(struct lwp *lp);
static void dummy_forking(struct lwp *plp, struct lwp *lp);
static void dummy_exiting(struct lwp *plp, struct proc *child);
static void dummy_uload_update(struct lwp *lp);
static void dummy_yield(struct lwp *lp);
static void dummy_changedcpu(struct lwp *lp);
struct usched usched_dummy = {
{ NULL },
"dummy", "Dummy DragonFly Scheduler",
NULL,
NULL,
dummy_acquire_curproc,
dummy_release_curproc,
dummy_setrunqueue,
dummy_schedulerclock,
dummy_recalculate_estcpu,
dummy_resetpriority,
dummy_forking,
dummy_exiting,
dummy_uload_update,
NULL,
dummy_yield,
dummy_changedcpu
};
struct usched_dummy_pcpu {
int rrcount;
struct thread *helper_thread;
struct lwp *uschedcp;
};
typedef struct usched_dummy_pcpu *dummy_pcpu_t;
static struct usched_dummy_pcpu dummy_pcpu[MAXCPU];
static cpumask_t dummy_curprocmask = CPUMASK_INITIALIZER_ALLONES;
static cpumask_t dummy_rdyprocmask;
static struct spinlock dummy_spin;
static TAILQ_HEAD(rq, lwp) dummy_runq;
static int dummy_runqcount;
static int usched_dummy_rrinterval = (ESTCPUFREQ + 9) / 10;
SYSCTL_INT(_kern, OID_AUTO, usched_dummy_rrinterval, CTLFLAG_RW,
&usched_dummy_rrinterval, 0, "");
static void
dummyinit(void *dummy)
{
TAILQ_INIT(&dummy_runq);
spin_init(&dummy_spin, "uscheddummy");
ATOMIC_CPUMASK_NANDBIT(dummy_curprocmask, 0);
}
SYSINIT(runqueue, SI_BOOT2_USCHED, SI_ORDER_FIRST, dummyinit, NULL);
static void
dummy_acquire_curproc(struct lwp *lp)
{
globaldata_t gd = mycpu;
dummy_pcpu_t dd = &dummy_pcpu[gd->gd_cpuid];
thread_t td = lp->lwp_thread;
if (user_resched_wanted())
dummy_select_curproc(gd);
if (dd->uschedcp == lp ||
(dd->uschedcp == NULL && TAILQ_EMPTY(&dummy_runq))) {
ATOMIC_CPUMASK_ORBIT(dummy_curprocmask, gd->gd_cpuid);
dd->uschedcp = lp;
return;
}
KKASSERT(dd->uschedcp != lp);
if (td->td_release)
td->td_release(lp->lwp_thread);
do {
crit_enter();
lwkt_deschedule_self(td);
dummy_setrunqueue(lp);
if ((td->td_flags & TDF_RUNQ) == 0)
++lp->lwp_ru.ru_nivcsw;
lwkt_switch();
crit_exit();
gd = mycpu;
dd = &dummy_pcpu[gd->gd_cpuid];
KKASSERT((lp->lwp_mpflags & LWP_MP_ONRUNQ) == 0);
} while (dd->uschedcp != lp);
}
static void
dummy_release_curproc(struct lwp *lp)
{
globaldata_t gd = mycpu;
dummy_pcpu_t dd = &dummy_pcpu[gd->gd_cpuid];
KKASSERT((lp->lwp_mpflags & LWP_MP_ONRUNQ) == 0);
if (dd->uschedcp == lp) {
dummy_select_curproc(gd);
}
}
static
void
dummy_select_curproc(globaldata_t gd)
{
dummy_pcpu_t dd = &dummy_pcpu[gd->gd_cpuid];
struct lwp *lp;
clear_user_resched();
spin_lock(&dummy_spin);
if ((lp = TAILQ_FIRST(&dummy_runq)) == NULL) {
dd->uschedcp = NULL;
ATOMIC_CPUMASK_NANDBIT(dummy_curprocmask, gd->gd_cpuid);
spin_unlock(&dummy_spin);
} else {
--dummy_runqcount;
TAILQ_REMOVE(&dummy_runq, lp, lwp_procq);
atomic_clear_int(&lp->lwp_mpflags, LWP_MP_ONRUNQ);
dd->uschedcp = lp;
ATOMIC_CPUMASK_ORBIT(dummy_curprocmask, gd->gd_cpuid);
spin_unlock(&dummy_spin);
lwkt_acquire(lp->lwp_thread);
lwkt_schedule(lp->lwp_thread);
}
}
static void
dummy_setrunqueue(struct lwp *lp)
{
globaldata_t gd = mycpu;
dummy_pcpu_t dd = &dummy_pcpu[gd->gd_cpuid];
cpumask_t mask;
int cpuid;
if (dd->uschedcp == NULL) {
dd->uschedcp = lp;
ATOMIC_CPUMASK_ORBIT(dummy_curprocmask, gd->gd_cpuid);
lwkt_schedule(lp->lwp_thread);
} else {
KKASSERT((lp->lwp_mpflags & LWP_MP_ONRUNQ) == 0);
spin_lock(&dummy_spin);
++dummy_runqcount;
TAILQ_INSERT_TAIL(&dummy_runq, lp, lwp_procq);
atomic_set_int(&lp->lwp_mpflags, LWP_MP_ONRUNQ);
lwkt_giveaway(lp->lwp_thread);
mask = dummy_rdyprocmask;
CPUMASK_NANDMASK(mask, dummy_curprocmask);
CPUMASK_ANDMASK(mask, gd->gd_other_cpus);
if (CPUMASK_TESTNZERO(mask)) {
cpuid = BSFCPUMASK(mask);
ATOMIC_CPUMASK_NANDBIT(dummy_rdyprocmask, cpuid);
spin_unlock(&dummy_spin);
lwkt_schedule(dummy_pcpu[cpuid].helper_thread);
} else {
spin_unlock(&dummy_spin);
}
}
}
static
void
dummy_schedulerclock(struct lwp *lp, sysclock_t period, sysclock_t cpstamp)
{
globaldata_t gd = mycpu;
dummy_pcpu_t dd = &dummy_pcpu[gd->gd_cpuid];
if (lp == NULL)
return;
if (++dd->rrcount >= usched_dummy_rrinterval) {
dd->rrcount = 0;
need_user_resched();
}
}
static
void
dummy_recalculate_estcpu(struct lwp *lp)
{
}
static
void
dummy_yield(struct lwp *lp)
{
need_user_resched();
}
static
void
dummy_changedcpu(struct lwp *lp __unused)
{
}
static void
dummy_resetpriority(struct lwp *lp)
{
switch(lp->lwp_rtprio.type) {
case RTP_PRIO_REALTIME:
lp->lwp_priority = PRIBASE_REALTIME + lp->lwp_rtprio.prio;
return;
case RTP_PRIO_NORMAL:
lp->lwp_priority = PRIBASE_NORMAL + lp->lwp_rtprio.prio;
break;
case RTP_PRIO_IDLE:
lp->lwp_priority = PRIBASE_IDLE + lp->lwp_rtprio.prio;
return;
case RTP_PRIO_THREAD:
lp->lwp_priority = PRIBASE_THREAD + lp->lwp_rtprio.prio;
return;
}
lp->lwp_thread->td_upri = -lp->lwp_priority;
}
static void
dummy_forking(struct lwp *plp, struct lwp *lp)
{
lp->lwp_estcpu = plp->lwp_estcpu;
#if 0
++plp->lwp_estcpu;
#endif
}
static void
dummy_exiting(struct lwp *plp, struct proc *child)
{
}
static void
dummy_uload_update(struct lwp *lp)
{
}
static void
dummy_sched_thread(void *dummy)
{
globaldata_t gd;
dummy_pcpu_t dd;
struct lwp *lp;
cpumask_t cpumask;
cpumask_t tmpmask;
int cpuid;
int tmpid;
gd = mycpu;
cpuid = gd->gd_cpuid;
dd = &dummy_pcpu[cpuid];
CPUMASK_ASSBIT(cpumask, cpuid);
for (;;) {
lwkt_deschedule_self(gd->gd_curthread);
ATOMIC_CPUMASK_ORBIT(dummy_rdyprocmask, cpuid);
spin_lock(&dummy_spin);
if (dd->uschedcp) {
tmpmask = dummy_rdyprocmask;
CPUMASK_NANDMASK(tmpmask, dummy_curprocmask);
CPUMASK_ANDMASK(tmpmask, gd->gd_other_cpus);
if (CPUMASK_TESTNZERO(tmpmask) && dummy_runqcount) {
tmpid = BSFCPUMASK(tmpmask);
KKASSERT(tmpid != cpuid);
ATOMIC_CPUMASK_NANDBIT(dummy_rdyprocmask, tmpid);
spin_unlock(&dummy_spin);
lwkt_schedule(dummy_pcpu[tmpid].helper_thread);
} else {
spin_unlock(&dummy_spin);
}
} else if ((lp = TAILQ_FIRST(&dummy_runq)) != NULL) {
--dummy_runqcount;
TAILQ_REMOVE(&dummy_runq, lp, lwp_procq);
atomic_clear_int(&lp->lwp_mpflags, LWP_MP_ONRUNQ);
dd->uschedcp = lp;
ATOMIC_CPUMASK_ORBIT(dummy_curprocmask, cpuid);
spin_unlock(&dummy_spin);
lwkt_acquire(lp->lwp_thread);
lwkt_schedule(lp->lwp_thread);
} else {
spin_unlock(&dummy_spin);
}
lwkt_switch();
}
}
static void
dummy_sched_thread_cpu_init(void)
{
int i;
if (bootverbose)
kprintf("start dummy scheduler helpers on cpus:");
for (i = 0; i < ncpus; ++i) {
dummy_pcpu_t dd = &dummy_pcpu[i];
cpumask_t mask;
CPUMASK_ASSBIT(mask, i);
if (CPUMASK_TESTMASK(mask, smp_active_mask) == 0)
continue;
if (bootverbose)
kprintf(" %d", i);
lwkt_create(dummy_sched_thread, NULL, &dd->helper_thread, NULL,
TDF_NOSTART, i, "dsched %d", i);
if (i)
ATOMIC_CPUMASK_NANDMASK(dummy_curprocmask, mask);
ATOMIC_CPUMASK_ORMASK(dummy_rdyprocmask, mask);
}
if (bootverbose)
kprintf("\n");
}
SYSINIT(uschedtd, SI_BOOT2_USCHED, SI_ORDER_SECOND,
dummy_sched_thread_cpu_init, NULL);