- Add support for CPU groups to ule. All SMT cores on the same physical
cpu are added to a group. - Don't place a cpu into the kseq_idle bitmask until all cpus in that group have idled. - Prefer idle groups over idle group members in the new kseq_transfer() function. In this way we will prefer to balance load across full cores rather than add further load a partial core. - Before a cpu goes idle, check the other group members for threads. Since SMT cpus may freely share threads, this is cheap. - SMT cores may be individually pinned and bound to now. This contrasts the old mechanism where binding or pinning would have allowed a thread to run on any available cpu. - Remove some unnecessary logic from sched_switch(). Priority propagation should be properly taken care of in sched_prio() now.
This commit is contained in:
parent
cf00356cc6
commit
7c857e9275
@ -203,9 +203,6 @@ struct td_sched *thread0_sched = &td_sched;
|
||||
/*
|
||||
* kseq - per processor runqs and statistics.
|
||||
*/
|
||||
|
||||
#define KSEQ_NCLASS (PRI_IDLE + 1) /* Number of run classes. */
|
||||
|
||||
struct kseq {
|
||||
struct runq ksq_idle; /* Queue of IDLE threads. */
|
||||
struct runq ksq_timeshare[2]; /* Run queues for !IDLE. */
|
||||
@ -216,23 +213,42 @@ struct kseq {
|
||||
short ksq_nice[SCHED_PRI_NRESV]; /* KSEs in each nice bin. */
|
||||
short ksq_nicemin; /* Least nice. */
|
||||
#ifdef SMP
|
||||
int ksq_load_transferable; /* kses that may be migrated. */
|
||||
int ksq_idled;
|
||||
int ksq_cpus; /* Count of CPUs in this kseq. */
|
||||
volatile struct kse *ksq_assigned; /* assigned by another CPU. */
|
||||
int ksq_transferable;
|
||||
LIST_ENTRY(kseq) ksq_siblings; /* Next in kseq group. */
|
||||
struct kseq_group *ksq_group; /* Our processor group. */
|
||||
volatile struct kse *ksq_assigned; /* assigned by another CPU. */
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* kseq groups are groups of processors which can cheaply share threads. When
|
||||
* one processor in the group goes idle it will check the runqs of the other
|
||||
* processors in its group prior to halting and waiting for an interrupt.
|
||||
* These groups are suitable for SMT (Symetric Multi-Threading) and not NUMA.
|
||||
* In a numa environment we'd want an idle bitmap per group and a two tiered
|
||||
* load balancer.
|
||||
*/
|
||||
struct kseq_group {
|
||||
int ksg_cpus; /* Count of CPUs in this kseq group. */
|
||||
int ksg_cpumask; /* Mask of cpus in this group. */
|
||||
int ksg_idlemask; /* Idle cpus in this group. */
|
||||
int ksg_mask; /* Bit mask for first cpu. */
|
||||
int ksg_transferable; /* Transferable load of this group. */
|
||||
LIST_HEAD(, kseq) ksg_members; /* Linked list of all members. */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* One kse queue per processor.
|
||||
*/
|
||||
#ifdef SMP
|
||||
static int kseq_idle;
|
||||
static struct kseq kseq_cpu[MAXCPU];
|
||||
static struct kseq *kseq_idmap[MAXCPU];
|
||||
#define KSEQ_SELF() (kseq_idmap[PCPU_GET(cpuid)])
|
||||
#define KSEQ_CPU(x) (kseq_idmap[(x)])
|
||||
#else
|
||||
static struct kseq_group kseq_groups[MAXCPU];
|
||||
#define KSEQ_SELF() (&kseq_cpu[PCPU_GET(cpuid)])
|
||||
#define KSEQ_CPU(x) (&kseq_cpu[(x)])
|
||||
#else /* !SMP */
|
||||
static struct kseq kseq_cpu;
|
||||
#define KSEQ_SELF() (&kseq_cpu)
|
||||
#define KSEQ_CPU(x) (&kseq_cpu)
|
||||
@ -256,13 +272,14 @@ static void kseq_nice_add(struct kseq *kseq, int nice);
|
||||
static void kseq_nice_rem(struct kseq *kseq, int nice);
|
||||
void kseq_print(int cpu);
|
||||
#ifdef SMP
|
||||
static int kseq_transfer(struct kseq *ksq, struct kse *ke, int class);
|
||||
static struct kse *runq_steal(struct runq *rq);
|
||||
static void sched_balance(void *arg);
|
||||
static void kseq_move(struct kseq *from, int cpu);
|
||||
static __inline void kseq_setidle(struct kseq *kseq);
|
||||
static int kseq_idled(struct kseq *kseq);
|
||||
static void kseq_notify(struct kse *ke, int cpu);
|
||||
static void kseq_assign(struct kseq *);
|
||||
static struct kse *kseq_steal(struct kseq *kseq);
|
||||
static struct kse *kseq_steal(struct kseq *kseq, int stealidle);
|
||||
#define KSE_CAN_MIGRATE(ke, class) \
|
||||
((class) != PRI_ITHD && (ke)->ke_thread->td_pinned == 0 && \
|
||||
((ke)->ke_flags & KEF_BOUND) == 0)
|
||||
@ -280,7 +297,7 @@ kseq_print(int cpu)
|
||||
printf("\tload: %d\n", kseq->ksq_load);
|
||||
printf("\tload TIMESHARE: %d\n", kseq->ksq_load_timeshare);
|
||||
#ifdef SMP
|
||||
printf("\tload transferable: %d\n", kseq->ksq_load_transferable);
|
||||
printf("\tload transferable: %d\n", kseq->ksq_transferable);
|
||||
#endif
|
||||
printf("\tnicemin:\t%d\n", kseq->ksq_nicemin);
|
||||
printf("\tnice counts:\n");
|
||||
@ -294,8 +311,10 @@ static __inline void
|
||||
kseq_runq_add(struct kseq *kseq, struct kse *ke)
|
||||
{
|
||||
#ifdef SMP
|
||||
if (KSE_CAN_MIGRATE(ke, PRI_BASE(ke->ke_ksegrp->kg_pri_class)))
|
||||
kseq->ksq_load_transferable++;
|
||||
if (KSE_CAN_MIGRATE(ke, PRI_BASE(ke->ke_ksegrp->kg_pri_class))) {
|
||||
kseq->ksq_transferable++;
|
||||
kseq->ksq_group->ksg_transferable++;
|
||||
}
|
||||
#endif
|
||||
runq_add(ke->ke_runq, ke);
|
||||
}
|
||||
@ -304,8 +323,10 @@ static __inline void
|
||||
kseq_runq_rem(struct kseq *kseq, struct kse *ke)
|
||||
{
|
||||
#ifdef SMP
|
||||
if (KSE_CAN_MIGRATE(ke, PRI_BASE(ke->ke_ksegrp->kg_pri_class)))
|
||||
kseq->ksq_load_transferable--;
|
||||
if (KSE_CAN_MIGRATE(ke, PRI_BASE(ke->ke_ksegrp->kg_pri_class))) {
|
||||
kseq->ksq_transferable--;
|
||||
kseq->ksq_group->ksg_transferable--;
|
||||
}
|
||||
#endif
|
||||
runq_remove(ke->ke_runq, ke);
|
||||
}
|
||||
@ -401,6 +422,7 @@ static void
|
||||
sched_balance(void *arg)
|
||||
{
|
||||
struct kseq *kseq;
|
||||
int transferable;
|
||||
int high_load;
|
||||
int low_load;
|
||||
int high_cpu;
|
||||
@ -422,8 +444,13 @@ sched_balance(void *arg)
|
||||
if (CPU_ABSENT(i) || (i & stopped_cpus) != 0)
|
||||
continue;
|
||||
kseq = KSEQ_CPU(i);
|
||||
if (kseq->ksq_load_transferable > high_load) {
|
||||
high_load = kseq->ksq_load_transferable;
|
||||
/*
|
||||
* Find the CPU with the highest load that has some threads
|
||||
* to transfer.
|
||||
*/
|
||||
if (kseq->ksq_load > high_load &&
|
||||
kseq->ksq_group->ksg_transferable) {
|
||||
high_load = kseq->ksq_load;
|
||||
high_cpu = i;
|
||||
}
|
||||
if (low_load == -1 || kseq->ksq_load < low_load) {
|
||||
@ -435,17 +462,28 @@ sched_balance(void *arg)
|
||||
/*
|
||||
* Nothing to do.
|
||||
*/
|
||||
if (high_load == 0 || low_load >= kseq->ksq_load)
|
||||
if (low_load >= high_load)
|
||||
goto out;
|
||||
/*
|
||||
* If we're transfering within a group we have to use this specific
|
||||
* kseq's transferable count, otherwise we can steal from other members
|
||||
* of the group.
|
||||
*/
|
||||
if (kseq->ksq_group == KSEQ_CPU(low_cpu)->ksq_group)
|
||||
transferable = kseq->ksq_transferable;
|
||||
else
|
||||
transferable = kseq->ksq_group->ksg_transferable;
|
||||
if (transferable == 0)
|
||||
goto out;
|
||||
/*
|
||||
* Determine what the imbalance is and then adjust that to how many
|
||||
* kses we actually have to give up (load_transferable).
|
||||
* kses we actually have to give up (transferable).
|
||||
*/
|
||||
diff = kseq->ksq_load - low_load;
|
||||
move = diff / 2;
|
||||
if (diff & 0x1)
|
||||
move++;
|
||||
move = min(move, high_load);
|
||||
move = min(move, transferable);
|
||||
for (i = 0; i < move; i++)
|
||||
kseq_move(kseq, low_cpu);
|
||||
out:
|
||||
@ -458,25 +496,75 @@ sched_balance(void *arg)
|
||||
static void
|
||||
kseq_move(struct kseq *from, int cpu)
|
||||
{
|
||||
struct kseq *kseq;
|
||||
struct kseq *to;
|
||||
struct kse *ke;
|
||||
|
||||
ke = kseq_steal(from);
|
||||
kseq = from;
|
||||
to = KSEQ_CPU(cpu);
|
||||
ke = kseq_steal(kseq, 1);
|
||||
if (ke == NULL) {
|
||||
struct kseq_group *ksg;
|
||||
|
||||
ksg = kseq->ksq_group;
|
||||
LIST_FOREACH(kseq, &ksg->ksg_members, ksq_siblings) {
|
||||
if (kseq == from || kseq->ksq_transferable == 0)
|
||||
continue;
|
||||
ke = kseq_steal(kseq, 1);
|
||||
break;
|
||||
}
|
||||
if (ke == NULL)
|
||||
panic("kseq_move: No KSEs available with a "
|
||||
"transferable count of %d\n",
|
||||
ksg->ksg_transferable);
|
||||
}
|
||||
if (kseq == to)
|
||||
return;
|
||||
ke->ke_state = KES_THREAD;
|
||||
kseq_runq_rem(from, ke);
|
||||
kseq_load_rem(from, ke);
|
||||
kseq_runq_rem(kseq, ke);
|
||||
kseq_load_rem(kseq, ke);
|
||||
|
||||
ke->ke_cpu = cpu;
|
||||
kseq_notify(ke, cpu);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
kseq_setidle(struct kseq *kseq)
|
||||
static int
|
||||
kseq_idled(struct kseq *kseq)
|
||||
{
|
||||
if (kseq->ksq_idled)
|
||||
return;
|
||||
kseq->ksq_idled = 1;
|
||||
atomic_set_int(&kseq_idle, PCPU_GET(cpumask));
|
||||
return;
|
||||
struct kseq_group *ksg;
|
||||
struct kseq *steal;
|
||||
struct kse *ke;
|
||||
|
||||
ksg = kseq->ksq_group;
|
||||
/*
|
||||
* If we're in a cpu group, try and steal kses from another cpu in
|
||||
* the group before idling.
|
||||
*/
|
||||
if (ksg->ksg_cpus > 1 && ksg->ksg_transferable) {
|
||||
LIST_FOREACH(steal, &ksg->ksg_members, ksq_siblings) {
|
||||
if (steal == kseq || steal->ksq_transferable == 0)
|
||||
continue;
|
||||
ke = kseq_steal(steal, 0);
|
||||
if (ke == NULL)
|
||||
continue;
|
||||
ke->ke_state = KES_THREAD;
|
||||
kseq_runq_rem(steal, ke);
|
||||
kseq_load_rem(steal, ke);
|
||||
ke->ke_cpu = PCPU_GET(cpuid);
|
||||
sched_add(ke->ke_thread);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We only set the idled bit when all of the cpus in the group are
|
||||
* idle. Otherwise we could get into a situation where a KSE bounces
|
||||
* back and forth between two idle cores on seperate physical CPUs.
|
||||
*/
|
||||
ksg->ksg_idlemask |= PCPU_GET(cpumask);
|
||||
if (ksg->ksg_idlemask != ksg->ksg_cpumask)
|
||||
return (1);
|
||||
atomic_set_int(&kseq_idle, ksg->ksg_mask);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -550,16 +638,69 @@ runq_steal(struct runq *rq)
|
||||
}
|
||||
|
||||
static struct kse *
|
||||
kseq_steal(struct kseq *kseq)
|
||||
kseq_steal(struct kseq *kseq, int stealidle)
|
||||
{
|
||||
struct kse *ke;
|
||||
|
||||
if ((ke = runq_steal(kseq->ksq_curr)) != NULL)
|
||||
return (ke);
|
||||
/*
|
||||
* Steal from next first to try to get a non-interactive task that
|
||||
* may not have run for a while.
|
||||
*/
|
||||
if ((ke = runq_steal(kseq->ksq_next)) != NULL)
|
||||
return (ke);
|
||||
return (runq_steal(&kseq->ksq_idle));
|
||||
if ((ke = runq_steal(kseq->ksq_curr)) != NULL)
|
||||
return (ke);
|
||||
if (stealidle)
|
||||
return (runq_steal(&kseq->ksq_idle));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
kseq_transfer(struct kseq *kseq, struct kse *ke, int class)
|
||||
{
|
||||
struct kseq_group *ksg;
|
||||
int cpu;
|
||||
|
||||
cpu = 0;
|
||||
ksg = kseq->ksq_group;
|
||||
|
||||
/*
|
||||
* XXX This ksg_transferable might work better if we were checking
|
||||
* against a global group load. As it is now, this prevents us from
|
||||
* transfering a thread from a group that is potentially bogged down
|
||||
* with non transferable load.
|
||||
*/
|
||||
if (ksg->ksg_transferable > ksg->ksg_cpus && kseq_idle) {
|
||||
/*
|
||||
* Multiple cpus could find this bit simultaneously
|
||||
* but the race shouldn't be terrible.
|
||||
*/
|
||||
cpu = ffs(kseq_idle);
|
||||
if (cpu)
|
||||
atomic_clear_int(&kseq_idle, 1 << (cpu - 1));
|
||||
}
|
||||
/*
|
||||
* If another cpu in this group has idled, assign a thread over
|
||||
* to them after checking to see if there are idled groups.
|
||||
*/
|
||||
if (cpu == 0 && kseq->ksq_load > 1 && ksg->ksg_idlemask) {
|
||||
cpu = ffs(ksg->ksg_idlemask);
|
||||
if (cpu)
|
||||
ksg->ksg_idlemask &= ~(1 << (cpu - 1));
|
||||
}
|
||||
/*
|
||||
* Now that we've found an idle CPU, migrate the thread.
|
||||
*/
|
||||
if (cpu) {
|
||||
cpu--;
|
||||
ke->ke_cpu = cpu;
|
||||
ke->ke_runq = NULL;
|
||||
kseq_notify(ke, cpu);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif /* SMP */
|
||||
|
||||
/*
|
||||
@ -616,11 +757,6 @@ kseq_setup(struct kseq *kseq)
|
||||
kseq->ksq_next = &kseq->ksq_timeshare[1];
|
||||
kseq->ksq_load = 0;
|
||||
kseq->ksq_load_timeshare = 0;
|
||||
#ifdef SMP
|
||||
kseq->ksq_load_transferable = 0;
|
||||
kseq->ksq_idled = 0;
|
||||
kseq->ksq_assigned = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@ -634,31 +770,64 @@ sched_setup(void *dummy)
|
||||
slice_max = (hz/7); /* ~140ms */
|
||||
|
||||
#ifdef SMP
|
||||
/* init kseqs */
|
||||
/* Create the idmap. */
|
||||
#ifdef ULE_HTT_EXPERIMENTAL
|
||||
/*
|
||||
* Initialize the kseqs.
|
||||
*/
|
||||
for (i = 0; i < MAXCPU; i++) {
|
||||
struct kseq *ksq;
|
||||
|
||||
ksq = &kseq_cpu[i];
|
||||
ksq->ksq_assigned = NULL;
|
||||
kseq_setup(&kseq_cpu[i]);
|
||||
}
|
||||
if (smp_topology == NULL) {
|
||||
#else
|
||||
if (1) {
|
||||
#endif
|
||||
struct kseq_group *ksg;
|
||||
struct kseq *ksq;
|
||||
|
||||
for (i = 0; i < MAXCPU; i++) {
|
||||
kseq_setup(&kseq_cpu[i]);
|
||||
kseq_idmap[i] = &kseq_cpu[i];
|
||||
kseq_cpu[i].ksq_cpus = 1;
|
||||
ksq = &kseq_cpu[i];
|
||||
ksg = &kseq_groups[i];
|
||||
/*
|
||||
* Setup a kse group with one member.
|
||||
*/
|
||||
ksq->ksq_transferable = 0;
|
||||
ksq->ksq_group = ksg;
|
||||
ksg->ksg_cpus = 1;
|
||||
ksg->ksg_idlemask = 0;
|
||||
ksg->ksg_cpumask = ksg->ksg_mask = 1 << i;
|
||||
ksg->ksg_transferable = 0;
|
||||
LIST_INIT(&ksg->ksg_members);
|
||||
LIST_INSERT_HEAD(&ksg->ksg_members, ksq, ksq_siblings);
|
||||
}
|
||||
} else {
|
||||
struct kseq_group *ksg;
|
||||
struct cpu_group *cg;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < smp_topology->ct_count; i++) {
|
||||
struct cpu_group *cg;
|
||||
|
||||
cg = &smp_topology->ct_group[i];
|
||||
kseq_setup(&kseq_cpu[i]);
|
||||
|
||||
for (j = 0; j < MAXCPU; j++)
|
||||
if ((cg->cg_mask & (1 << j)) != 0)
|
||||
kseq_idmap[j] = &kseq_cpu[i];
|
||||
kseq_cpu[i].ksq_cpus = cg->cg_count;
|
||||
ksg = &kseq_groups[i];
|
||||
/*
|
||||
* Initialize the group.
|
||||
*/
|
||||
ksg->ksg_idlemask = 0;
|
||||
ksg->ksg_transferable = 0;
|
||||
ksg->ksg_cpus = cg->cg_count;
|
||||
ksg->ksg_cpumask = cg->cg_mask;
|
||||
LIST_INIT(&ksg->ksg_members);
|
||||
/*
|
||||
* Find all of the group members and add them.
|
||||
*/
|
||||
for (j = 0; j < MAXCPU; j++) {
|
||||
if ((cg->cg_mask & (1 << j)) != 0) {
|
||||
if (ksg->ksg_mask == 0)
|
||||
ksg->ksg_mask = 1 << j;
|
||||
kseq_cpu[j].ksq_transferable = 0;
|
||||
kseq_cpu[j].ksq_group = ksg;
|
||||
LIST_INSERT_HEAD(&ksg->ksg_members,
|
||||
&kseq_cpu[j], ksq_siblings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callout_init(&kseq_lb_callout, CALLOUT_MPSAFE);
|
||||
@ -897,20 +1066,8 @@ sched_switch(struct thread *td)
|
||||
if (td->td_proc->p_flag & P_SA) {
|
||||
kseq_load_rem(KSEQ_CPU(ke->ke_cpu), ke);
|
||||
setrunqueue(td);
|
||||
} else {
|
||||
/*
|
||||
* This queue is always correct except for idle threads
|
||||
* which have a higher priority due to priority
|
||||
* propagation.
|
||||
*/
|
||||
if (ke->ke_ksegrp->kg_pri_class == PRI_IDLE) {
|
||||
if (td->td_priority < PRI_MIN_IDLE)
|
||||
ke->ke_runq = KSEQ_SELF()->ksq_curr;
|
||||
else
|
||||
ke->ke_runq = &KSEQ_SELF()->ksq_idle;
|
||||
}
|
||||
} else
|
||||
kseq_runq_add(KSEQ_SELF(), ke);
|
||||
}
|
||||
} else {
|
||||
if (ke->ke_runq)
|
||||
kseq_load_rem(KSEQ_CPU(ke->ke_cpu), ke);
|
||||
@ -1078,10 +1235,14 @@ sched_class(struct ksegrp *kg, int class)
|
||||
* class.
|
||||
*/
|
||||
if (ke->ke_state == KES_ONRUNQ) {
|
||||
if (KSE_CAN_MIGRATE(ke, oclass))
|
||||
kseq->ksq_load_transferable--;
|
||||
if (KSE_CAN_MIGRATE(ke, nclass))
|
||||
kseq->ksq_load_transferable++;
|
||||
if (KSE_CAN_MIGRATE(ke, oclass)) {
|
||||
kseq->ksq_transferable--;
|
||||
kseq->ksq_group->ksg_transferable--;
|
||||
}
|
||||
if (KSE_CAN_MIGRATE(ke, nclass)) {
|
||||
kseq->ksq_transferable++;
|
||||
kseq->ksq_group->ksg_transferable++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (oclass == PRI_TIMESHARE) {
|
||||
@ -1251,6 +1412,7 @@ sched_choose(void)
|
||||
mtx_assert(&sched_lock, MA_OWNED);
|
||||
kseq = KSEQ_SELF();
|
||||
#ifdef SMP
|
||||
restart:
|
||||
if (kseq->ksq_assigned)
|
||||
kseq_assign(kseq);
|
||||
#endif
|
||||
@ -1258,7 +1420,8 @@ sched_choose(void)
|
||||
if (ke) {
|
||||
#ifdef SMP
|
||||
if (ke->ke_ksegrp->kg_pri_class == PRI_IDLE)
|
||||
kseq_setidle(kseq);
|
||||
if (kseq_idled(kseq) == 0)
|
||||
goto restart;
|
||||
#endif
|
||||
kseq_runq_rem(kseq, ke);
|
||||
ke->ke_state = KES_THREAD;
|
||||
@ -1271,7 +1434,8 @@ sched_choose(void)
|
||||
return (ke);
|
||||
}
|
||||
#ifdef SMP
|
||||
kseq_setidle(kseq);
|
||||
if (kseq_idled(kseq) == 0)
|
||||
goto restart;
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
@ -1310,24 +1474,12 @@ sched_add(struct thread *td)
|
||||
ke->ke_cpu = PCPU_GET(cpuid);
|
||||
break;
|
||||
case PRI_TIMESHARE:
|
||||
#ifdef SMP
|
||||
if (ke->ke_cpu != PCPU_GET(cpuid)) {
|
||||
kseq_notify(ke, ke->ke_cpu);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (SCHED_CURR(kg, ke))
|
||||
ke->ke_runq = kseq->ksq_curr;
|
||||
else
|
||||
ke->ke_runq = kseq->ksq_next;
|
||||
break;
|
||||
case PRI_IDLE:
|
||||
#ifdef SMP
|
||||
if (ke->ke_cpu != PCPU_GET(cpuid)) {
|
||||
kseq_notify(ke, ke->ke_cpu);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* This is for priority prop.
|
||||
*/
|
||||
@ -1342,35 +1494,33 @@ sched_add(struct thread *td)
|
||||
break;
|
||||
}
|
||||
#ifdef SMP
|
||||
if (ke->ke_cpu != PCPU_GET(cpuid)) {
|
||||
kseq_notify(ke, ke->ke_cpu);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If there are any idle processors, give them our extra load. The
|
||||
* If there are any idle groups, give them our extra load. The
|
||||
* threshold at which we start to reassign kses has a large impact
|
||||
* on the overall performance of the system. Tuned too high and
|
||||
* some CPUs may idle. Too low and there will be excess migration
|
||||
* and context swiches.
|
||||
*/
|
||||
if (kseq->ksq_load_transferable > kseq->ksq_cpus &&
|
||||
KSE_CAN_MIGRATE(ke, class) && kseq_idle) {
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* Multiple cpus could find this bit simultaneously but the
|
||||
* race shouldn't be terrible.
|
||||
*/
|
||||
cpu = ffs(kseq_idle);
|
||||
if (cpu) {
|
||||
cpu--;
|
||||
atomic_clear_int(&kseq_idle, 1 << cpu);
|
||||
ke->ke_cpu = cpu;
|
||||
ke->ke_runq = NULL;
|
||||
kseq_notify(ke, cpu);
|
||||
if (kseq->ksq_load > 1 && KSE_CAN_MIGRATE(ke, class))
|
||||
if (kseq_transfer(kseq, ke, class))
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (kseq->ksq_idled &&
|
||||
(class == PRI_TIMESHARE || class == PRI_REALTIME)) {
|
||||
atomic_clear_int(&kseq_idle, PCPU_GET(cpumask));
|
||||
kseq->ksq_idled = 0;
|
||||
if ((class == PRI_TIMESHARE || class == PRI_REALTIME) &&
|
||||
(kseq->ksq_group->ksg_idlemask & PCPU_GET(cpumask)) != 0) {
|
||||
/*
|
||||
* Check to see if our group is unidling, and if so, remove it
|
||||
* from the global idle mask.
|
||||
*/
|
||||
if (kseq->ksq_group->ksg_idlemask ==
|
||||
kseq->ksq_group->ksg_cpumask)
|
||||
atomic_clear_int(&kseq_idle, kseq->ksq_group->ksg_mask);
|
||||
/*
|
||||
* Now remove ourselves from the group specific idle mask.
|
||||
*/
|
||||
kseq->ksq_group->ksg_idlemask &= ~PCPU_GET(cpumask);
|
||||
}
|
||||
#endif
|
||||
if (td->td_priority < curthread->td_priority)
|
||||
@ -1448,13 +1598,10 @@ sched_bind(struct thread *td, int cpu)
|
||||
|
||||
mtx_assert(&sched_lock, MA_OWNED);
|
||||
ke = td->td_kse;
|
||||
#ifndef SMP
|
||||
ke->ke_flags |= KEF_BOUND;
|
||||
#else
|
||||
if (PCPU_GET(cpuid) == cpu) {
|
||||
ke->ke_flags |= KEF_BOUND;
|
||||
#ifdef SMP
|
||||
if (PCPU_GET(cpuid) == cpu)
|
||||
return;
|
||||
}
|
||||
/* sched_rem without the runq_remove */
|
||||
ke->ke_state = KES_THREAD;
|
||||
ke->ke_ksegrp->kg_runq_kses--;
|
||||
|
Loading…
Reference in New Issue
Block a user