1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-29 16:44:03 +00:00

SCHED_4BSD scheduling quantum mechanism appears to be broken for some time.

With switchticks variable being reset each time thread preempted (that is
done regularly by interrupt threads) scheduling quantum may never expire.
It was not noticed in time because several other factors still regularly
trigger context switches.

Handle the problem by replacing that mechanism with its equivalent from
SCHED_ULE called time slice. It is effectively the same, just measured in
context of stathz instead of hz. Some unification is probably not bad.
This commit is contained in:
Alexander Motin 2012-08-09 18:09:59 +00:00
parent b3ca34cfd2
commit 48317e9e27
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=239153

View File

@ -94,6 +94,7 @@ struct td_sched {
fixpt_t ts_pctcpu; /* (j) %cpu during p_swtime. */ fixpt_t ts_pctcpu; /* (j) %cpu during p_swtime. */
int ts_cpticks; /* (j) Ticks of cpu time. */ int ts_cpticks; /* (j) Ticks of cpu time. */
int ts_slptime; /* (j) Seconds !RUNNING. */ int ts_slptime; /* (j) Seconds !RUNNING. */
int ts_slice; /* Remaining part of time slice. */
int ts_flags; int ts_flags;
struct runq *ts_runq; /* runq the thread is currently on */ struct runq *ts_runq; /* runq the thread is currently on */
#ifdef KTR #ifdef KTR
@ -117,9 +118,9 @@ struct td_sched {
static struct td_sched td_sched0; static struct td_sched td_sched0;
struct mtx sched_lock; struct mtx sched_lock;
static int realstathz; /* stathz is sometimes 0 and run off of hz. */
static int sched_tdcnt; /* Total runnable threads in the system. */ static int sched_tdcnt; /* Total runnable threads in the system. */
static int sched_quantum; /* Roundrobin scheduling quantum in ticks. */ static int sched_slice = 1; /* Thread run time before rescheduling. */
#define SCHED_QUANTUM (hz / 10) /* Default sched quantum */
static void setup_runqs(void); static void setup_runqs(void);
static void schedcpu(void); static void schedcpu(void);
@ -145,6 +146,10 @@ SYSINIT(schedcpu, SI_SUB_RUN_SCHEDULER, SI_ORDER_FIRST, kproc_start,
&sched_kp); &sched_kp);
SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL); SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL);
static void sched_initticks(void *dummy);
SYSINIT(sched_initticks, SI_SUB_CLOCKS, SI_ORDER_THIRD, sched_initticks,
NULL);
/* /*
* Global run queue. * Global run queue.
*/ */
@ -179,31 +184,12 @@ setup_runqs(void)
runq_init(&runq); runq_init(&runq);
} }
static int
sysctl_kern_quantum(SYSCTL_HANDLER_ARGS)
{
int error, new_val;
new_val = sched_quantum * tick;
error = sysctl_handle_int(oidp, &new_val, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
if (new_val < tick)
return (EINVAL);
sched_quantum = new_val / tick;
hogticks = 2 * sched_quantum;
return (0);
}
SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RD, 0, "Scheduler"); SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RD, 0, "Scheduler");
SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, "4BSD", 0, SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, "4BSD", 0,
"Scheduler name"); "Scheduler name");
SYSCTL_INT(_kern_sched, OID_AUTO, slice, CTLFLAG_RW, &sched_slice, 0,
SYSCTL_PROC(_kern_sched, OID_AUTO, quantum, CTLTYPE_INT | CTLFLAG_RW, "Slice size for timeshare threads");
0, sizeof sched_quantum, sysctl_kern_quantum, "I",
"Roundrobin scheduling quantum in microseconds");
#ifdef SMP #ifdef SMP
/* Enable forwarding of wakeups to all other cpus */ /* Enable forwarding of wakeups to all other cpus */
static SYSCTL_NODE(_kern_sched, OID_AUTO, ipiwakeup, CTLFLAG_RD, NULL, static SYSCTL_NODE(_kern_sched, OID_AUTO, ipiwakeup, CTLFLAG_RD, NULL,
@ -471,9 +457,8 @@ schedcpu(void)
struct thread *td; struct thread *td;
struct proc *p; struct proc *p;
struct td_sched *ts; struct td_sched *ts;
int awake, realstathz; int awake;
realstathz = stathz ? stathz : hz;
sx_slock(&allproc_lock); sx_slock(&allproc_lock);
FOREACH_PROC_IN_SYSTEM(p) { FOREACH_PROC_IN_SYSTEM(p) {
PROC_LOCK(p); PROC_LOCK(p);
@ -645,14 +630,28 @@ sched_setup(void *dummy)
{ {
setup_runqs(); setup_runqs();
if (sched_quantum == 0) /*
sched_quantum = SCHED_QUANTUM; * To avoid divide-by-zero, we set realstathz a dummy value
hogticks = 2 * sched_quantum; * in case which sched_clock() called before sched_initticks().
*/
realstathz = hz;
sched_slice = realstathz / 10; /* ~100ms */
/* Account for thread0. */ /* Account for thread0. */
sched_load_add(); sched_load_add();
} }
/*
* This routine determines the sched_slice after stathz and hz are setup.
*/
static void
sched_initticks(void *dummy)
{
realstathz = stathz ? stathz : hz;
sched_slice = realstathz / 10; /* ~100ms */
}
/* External interfaces start here */ /* External interfaces start here */
/* /*
@ -670,6 +669,7 @@ schedinit(void)
proc0.p_sched = NULL; /* XXX */ proc0.p_sched = NULL; /* XXX */
thread0.td_sched = &td_sched0; thread0.td_sched = &td_sched0;
thread0.td_lock = &sched_lock; thread0.td_lock = &sched_lock;
td_sched0.ts_slice = sched_slice;
mtx_init(&sched_lock, "sched lock", NULL, MTX_SPIN | MTX_RECURSE); mtx_init(&sched_lock, "sched lock", NULL, MTX_SPIN | MTX_RECURSE);
} }
@ -686,9 +686,9 @@ sched_runnable(void)
int int
sched_rr_interval(void) sched_rr_interval(void)
{ {
if (sched_quantum == 0)
sched_quantum = SCHED_QUANTUM; /* Convert sched_slice from stathz to hz. */
return (sched_quantum); return (hz / (realstathz / sched_slice));
} }
/* /*
@ -725,9 +725,10 @@ sched_clock(struct thread *td)
* Force a context switch if the current thread has used up a full * Force a context switch if the current thread has used up a full
* quantum (default quantum is 100ms). * quantum (default quantum is 100ms).
*/ */
if (!TD_IS_IDLETHREAD(td) && if (!TD_IS_IDLETHREAD(td) && (--ts->ts_slice <= 0)) {
ticks - PCPU_GET(switchticks) >= sched_quantum) ts->ts_slice = sched_slice;
td->td_flags |= TDF_NEEDRESCHED; td->td_flags |= TDF_NEEDRESCHED;
}
stat = DPCPU_PTR(idlestat); stat = DPCPU_PTR(idlestat);
stat->oldidlecalls = stat->idlecalls; stat->oldidlecalls = stat->idlecalls;
@ -781,6 +782,7 @@ sched_fork_thread(struct thread *td, struct thread *childtd)
ts = childtd->td_sched; ts = childtd->td_sched;
bzero(ts, sizeof(*ts)); bzero(ts, sizeof(*ts));
ts->ts_flags |= (td->td_sched->ts_flags & TSF_AFFINITY); ts->ts_flags |= (td->td_sched->ts_flags & TSF_AFFINITY);
ts->ts_slice = 1;
} }
void void
@ -1077,6 +1079,7 @@ sched_wakeup(struct thread *td)
} }
td->td_slptick = 0; td->td_slptick = 0;
ts->ts_slptime = 0; ts->ts_slptime = 0;
ts->ts_slice = sched_slice;
sched_add(td, SRQ_BORING); sched_add(td, SRQ_BORING);
} }