1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-19 10:53:58 +00:00

- Keep seperate statistics and run queues for different scheduling classes.

- Treat each class specially in kseq_{choose,add,rem}.  Let the rest of the
   code be less aware of scheduling classes.
 - Skip the interactivity calculation for non TIMESHARE ksegrps.
 - Move slice and runq selection into kseq_add().  Uninline it now that it's
   big.
This commit is contained in:
Jeff Roberson 2003-04-03 00:29:28 +00:00
parent cc66ebe2a9
commit a8949de20e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=112994

View File

@ -188,10 +188,14 @@ struct td_sched *thread0_sched = &td_sched;
*/ */
struct kseq { struct kseq {
struct runq ksq_runqs[2]; struct runq ksq_ithd; /* Queue of ITHD and REALTIME tds. */
struct runq ksq_idle; /* Queue of IDLE threads. */
struct runq ksq_runqs[2]; /* Run queues for TIMESHARE. */
struct runq *ksq_curr; struct runq *ksq_curr;
struct runq *ksq_next; struct runq *ksq_next;
int ksq_load; /* Total runnable */ int ksq_itload; /* Total runnable for ITHD. */
int ksq_tsload; /* Total runnable for TIMESHARE. */
int ksq_idload; /* Total runnable for IDLE. */
#ifdef SMP #ifdef SMP
unsigned int ksq_rslices; /* Slices on run queue */ unsigned int ksq_rslices; /* Slices on run queue */
unsigned int ksq_bload; /* Threads waiting on IO */ unsigned int ksq_bload; /* Threads waiting on IO */
@ -221,7 +225,7 @@ int sched_pickcpu(void);
static struct kse * kseq_choose(struct kseq *kseq); static struct kse * kseq_choose(struct kseq *kseq);
static int kseq_nice_min(struct kseq *kseq); static int kseq_nice_min(struct kseq *kseq);
static void kseq_setup(struct kseq *kseq); static void kseq_setup(struct kseq *kseq);
static __inline void kseq_add(struct kseq *kseq, struct kse *ke); static void kseq_add(struct kseq *kseq, struct kse *ke);
static __inline void kseq_rem(struct kseq *kseq, struct kse *ke); static __inline void kseq_rem(struct kseq *kseq, struct kse *ke);
#ifdef SMP #ifdef SMP
static __inline void kseq_sleep(struct kseq *kseq, struct kse *ke); static __inline void kseq_sleep(struct kseq *kseq, struct kse *ke);
@ -229,19 +233,84 @@ static __inline void kseq_wakeup(struct kseq *kseq, struct kse *ke);
struct kseq * kseq_load_highest(void); struct kseq * kseq_load_highest(void);
#endif #endif
static __inline void static void
kseq_add(struct kseq *kseq, struct kse *ke) kseq_add(struct kseq *kseq, struct kse *ke)
{ {
struct ksegrp *kg;
kg = ke->ke_ksegrp;
/*
* Figure out what run queue we should go on and assign a slice.
*/
switch (kg->kg_pri_class) {
/*
* If we're a real-time or interrupt thread place us on the curr
* queue for the current processor. Hopefully this will yield the
* lowest latency response.
*/
case PRI_ITHD:
case PRI_REALTIME:
ke->ke_runq = &kseq->ksq_ithd;
ke->ke_slice = SCHED_SLICE_MAX;
kseq->ksq_itload++;
break;
/*
* Timeshare threads get placed on the appropriate queue on their
* bound cpu.
*/
case PRI_TIMESHARE:
if (ke->ke_runq == NULL) {
if (SCHED_CURR(kg))
ke->ke_runq = kseq->ksq_curr;
else
ke->ke_runq = kseq->ksq_next;
}
if (ke->ke_slice == 0)
sched_slice(ke);
kseq->ksq_tsload++;
break;
/*
* Only grant PRI_IDLE processes a slice if there is nothing else
* running.
*/
case PRI_IDLE:
ke->ke_runq = &kseq->ksq_idle;
ke->ke_slice = SCHED_SLICE_MIN;
kseq->ksq_idload++;
break;
default:
panic("Unknown priority class.\n");
break;
}
runq_add(ke->ke_runq, ke); runq_add(ke->ke_runq, ke);
kseq->ksq_load++;
#ifdef SMP #ifdef SMP
kseq->ksq_rslices += ke->ke_slice; kseq->ksq_rslices += ke->ke_slice;
#endif #endif
} }
static __inline void static void
kseq_rem(struct kseq *kseq, struct kse *ke) kseq_rem(struct kseq *kseq, struct kse *ke)
{ {
kseq->ksq_load--; struct ksegrp *kg;
kg = ke->ke_ksegrp;
/*
* XXX Consider making the load an array.
*/
switch (kg->kg_pri_class) {
case PRI_ITHD:
case PRI_REALTIME:
kseq->ksq_itload--;
break;
case PRI_TIMESHARE:
kseq->ksq_tsload--;
break;
case PRI_IDLE:
kseq->ksq_idload--;
break;
}
runq_remove(ke->ke_runq, ke); runq_remove(ke->ke_runq, ke);
#ifdef SMP #ifdef SMP
kseq->ksq_rslices -= ke->ke_slice; kseq->ksq_rslices -= ke->ke_slice;
@ -276,8 +345,8 @@ kseq_load_highest(void)
if (CPU_ABSENT(i)) if (CPU_ABSENT(i))
continue; continue;
kseq = KSEQ_CPU(i); kseq = KSEQ_CPU(i);
if (kseq->ksq_load > load) { if (kseq->ksq_tsload > load) {
load = kseq->ksq_load; load = kseq->ksq_tsload;
cpu = i; cpu = i;
} }
} }
@ -294,14 +363,23 @@ kseq_choose(struct kseq *kseq)
struct kse *ke; struct kse *ke;
struct runq *swap; struct runq *swap;
if ((ke = runq_choose(kseq->ksq_curr)) == NULL) { if (kseq->ksq_itload)
return (runq_choose(&kseq->ksq_ithd));
if (kseq->ksq_tsload) {
if ((ke = runq_choose(kseq->ksq_curr)) != NULL)
return (ke);
swap = kseq->ksq_curr; swap = kseq->ksq_curr;
kseq->ksq_curr = kseq->ksq_next; kseq->ksq_curr = kseq->ksq_next;
kseq->ksq_next = swap; kseq->ksq_next = swap;
ke = runq_choose(kseq->ksq_curr);
}
return (ke); return (runq_choose(kseq->ksq_curr));
}
if (kseq->ksq_idload)
return (runq_choose(&kseq->ksq_idle));
return (NULL);
} }
static int static int
@ -310,7 +388,7 @@ kseq_nice_min(struct kseq *kseq)
struct kse *ke0; struct kse *ke0;
struct kse *ke1; struct kse *ke1;
if (kseq->ksq_load == 0) if (kseq->ksq_tsload == 0)
return (0); return (0);
ke0 = runq_choose(kseq->ksq_curr); ke0 = runq_choose(kseq->ksq_curr);
@ -330,9 +408,13 @@ kseq_setup(struct kseq *kseq)
{ {
kseq->ksq_curr = &kseq->ksq_runqs[0]; kseq->ksq_curr = &kseq->ksq_runqs[0];
kseq->ksq_next = &kseq->ksq_runqs[1]; kseq->ksq_next = &kseq->ksq_runqs[1];
runq_init(&kseq->ksq_ithd);
runq_init(kseq->ksq_curr); runq_init(kseq->ksq_curr);
runq_init(kseq->ksq_next); runq_init(kseq->ksq_next);
kseq->ksq_load = 0; runq_init(&kseq->ksq_idle);
kseq->ksq_itload = 0;
kseq->ksq_tsload = 0;
kseq->ksq_idload = 0;
#ifdef SMP #ifdef SMP
kseq->ksq_rslices = 0; kseq->ksq_rslices = 0;
kseq->ksq_bload = 0; kseq->ksq_bload = 0;
@ -381,7 +463,7 @@ sched_priority(struct ksegrp *kg)
/* /*
* Calculate a time slice based on the properties of the kseg and the runq * Calculate a time slice based on the properties of the kseg and the runq
* that we're on. * that we're on. This is only for PRI_TIMESHARE ksegrps.
*/ */
static void static void
sched_slice(struct kse *ke) sched_slice(struct kse *ke)
@ -422,7 +504,7 @@ sched_slice(struct kse *ke)
nice_base = kseq_nice_min(kseq); nice_base = kseq_nice_min(kseq);
nice = kg->kg_nice + (0 - nice_base); nice = kg->kg_nice + (0 - nice_base);
if (kseq->ksq_load == 0 || kg->kg_nice < nice_base) if (kseq->ksq_tsload == 0 || kg->kg_nice < nice_base)
ke->ke_slice = SCHED_SLICE_MAX; ke->ke_slice = SCHED_SLICE_MAX;
else if (nice <= SCHED_PRI_NHALF) else if (nice <= SCHED_PRI_NHALF)
ke->ke_slice = SCHED_SLICE_NICE(nice); ke->ke_slice = SCHED_SLICE_NICE(nice);
@ -432,9 +514,9 @@ sched_slice(struct kse *ke)
ke->ke_slice = SCHED_SLICE_MIN; ke->ke_slice = SCHED_SLICE_MIN;
/* /*
* Every time we grant a new slice check to see if we need to scale * Check to see if we need to scale back the slp and run time
* back the slp and run time in the kg. This will cause us to forget * in the kg. This will cause us to forget old interactivity
* old interactivity while maintaining the current ratio. * while maintaining the current ratio.
*/ */
if ((kg->kg_runtime + kg->kg_slptime) > SCHED_SLP_RUN_MAX) { if ((kg->kg_runtime + kg->kg_slptime) > SCHED_SLP_RUN_MAX) {
kg->kg_runtime /= SCHED_SLP_RUN_THROTTLE; kg->kg_runtime /= SCHED_SLP_RUN_THROTTLE;
@ -516,9 +598,9 @@ sched_pickcpu(void)
if (CPU_ABSENT(i)) if (CPU_ABSENT(i))
continue; continue;
kseq = KSEQ_CPU(i); kseq = KSEQ_CPU(i);
if (kseq->ksq_load < load) { if (kseq->ksq_tsload < load) {
cpu = i; cpu = i;
load = kseq->ksq_load; load = kseq->ksq_tsload;
} }
} }
@ -707,9 +789,11 @@ sched_exit(struct ksegrp *kg, struct ksegrp *child)
{ {
/* XXX Need something better here */ /* XXX Need something better here */
mtx_assert(&sched_lock, MA_OWNED); mtx_assert(&sched_lock, MA_OWNED);
#if 0
kg->kg_slptime = child->kg_slptime; kg->kg_slptime = child->kg_slptime;
kg->kg_runtime = child->kg_runtime; kg->kg_runtime = child->kg_runtime;
sched_priority(kg); sched_priority(kg);
#endif
} }
void void
@ -732,6 +816,7 @@ sched_clock(struct thread *td)
/* Adjust ticks for pctcpu */ /* Adjust ticks for pctcpu */
ke->ke_ticks++; ke->ke_ticks++;
ke->ke_ltick = ticks; ke->ke_ltick = ticks;
/* Go up to one second beyond our max and then trim back down */ /* Go up to one second beyond our max and then trim back down */
if (ke->ke_ftick + SCHED_CPU_TICKS + hz < ke->ke_ltick) if (ke->ke_ftick + SCHED_CPU_TICKS + hz < ke->ke_ltick)
sched_pctcpu_update(ke); sched_pctcpu_update(ke);
@ -751,6 +836,11 @@ sched_clock(struct thread *td)
nke->ke_thread->td_priority < td->td_priority) nke->ke_thread->td_priority < td->td_priority)
td->td_flags |= TDF_NEEDRESCHED; td->td_flags |= TDF_NEEDRESCHED;
#endif #endif
/*
* We only do slicing code for TIMESHARE ksegrps.
*/
if (kg->kg_pri_class != PRI_TIMESHARE)
return;
/* /*
* We used a tick charge it to the ksegrp so that we can compute our * We used a tick charge it to the ksegrp so that we can compute our
* "interactivity". * "interactivity".
@ -762,11 +852,11 @@ sched_clock(struct thread *td)
*/ */
ke->ke_slice--; ke->ke_slice--;
/* /*
* We're out of time, recompute priorities and requeue * We're out of time, recompute priorities and requeue. We'll get a
* new slice when we're put back on the run queue.
*/ */
if (ke->ke_slice <= 0) { if (ke->ke_slice <= 0) {
sched_priority(kg); sched_priority(kg);
sched_slice(ke);
td->td_flags |= TDF_NEEDRESCHED; td->td_flags |= TDF_NEEDRESCHED;
ke->ke_runq = NULL; ke->ke_runq = NULL;
} }
@ -779,7 +869,7 @@ sched_runnable(void)
kseq = KSEQ_SELF(); kseq = KSEQ_SELF();
if (kseq->ksq_load) if (kseq->ksq_tsload || kseq->ksq_idload || kseq->ksq_itload)
return (1); return (1);
#ifdef SMP #ifdef SMP
/* /*
@ -798,7 +888,7 @@ sched_runnable(void)
if (CPU_ABSENT(i)) if (CPU_ABSENT(i))
continue; continue;
kseq = KSEQ_CPU(i); kseq = KSEQ_CPU(i);
if (kseq->ksq_load) if (kseq->ksq_tsload)
return (1); return (1);
} }
} }
@ -836,11 +926,12 @@ sched_choose(void)
/* /*
* If we dequeue a kse with a slice of zero it was below the * If we dequeue a kse with a slice of zero it was below the
* nice threshold to acquire a slice. Recalculate the slice * nice threshold to acquire a slice. Force it on to the
* to see if the situation has changed and then requeue. * next run queue and let kseq_add() pick a new slice.
*
* XXX This code should live in a TIMESHARE specific section.
*/ */
if (ke->ke_slice == 0) { if (ke->ke_slice == 0) {
sched_slice(ke);
ke->ke_runq = kseq->ksq_next; ke->ke_runq = kseq->ksq_next;
kseq_add(kseq, ke); kseq_add(kseq, ke);
goto retry; goto retry;
@ -859,6 +950,11 @@ sched_choose(void)
kseq = kseq_load_highest(); kseq = kseq_load_highest();
if (kseq == NULL) if (kseq == NULL)
return (NULL); return (NULL);
/*
* XXX Do we want to migrate interrupt or realtime threads?
* Currently we'll only try to steal if there is a TIMESHARE
* thread available but we will steal a REALTIME or interrupt
*/
ke = kseq_choose(kseq); ke = kseq_choose(kseq);
kseq_rem(kseq, ke); kseq_rem(kseq, ke);
@ -885,28 +981,18 @@ sched_add(struct kse *ke)
KASSERT(ke->ke_proc->p_sflag & PS_INMEM, KASSERT(ke->ke_proc->p_sflag & PS_INMEM,
("sched_add: process swapped out")); ("sched_add: process swapped out"));
/* switch (ke->ke_ksegrp->kg_pri_class) {
* Timeshare threads get placed on the appropriate queue on their case PRI_ITHD:
* bound cpu. case PRI_REALTIME:
*/
if (ke->ke_ksegrp->kg_pri_class == PRI_TIMESHARE) {
kseq = KSEQ_CPU(ke->ke_cpu);
if (ke->ke_runq == NULL) {
if (SCHED_CURR(ke->ke_ksegrp))
ke->ke_runq = kseq->ksq_curr;
else
ke->ke_runq = kseq->ksq_next;
}
/*
* If we're a real-time or interrupt thread place us on the curr
* queue for the current processor. Hopefully this will yield the
* lowest latency response.
*/
} else {
kseq = KSEQ_SELF(); kseq = KSEQ_SELF();
ke->ke_runq = kseq->ksq_curr; break;
case PRI_TIMESHARE:
case PRI_IDLE:
default:
kseq = KSEQ_CPU(ke->ke_cpu);
break;
} }
ke->ke_ksegrp->kg_runq_kses++; ke->ke_ksegrp->kg_runq_kses++;
ke->ke_state = KES_ONRUNQ; ke->ke_state = KES_ONRUNQ;