commit 09725f8af37415c30a1a53d4a34e67fabcba105d Author: Dario Faggioli Date: Wed Feb 8 19:01:53 2017 +0100 xen: credit2: never consider CPUs outside of our cpupool. In fact, relying on the mask of what pCPUs belong to which Credit2 runqueue is not enough. If we only do that, when Credit2 is the boot scheduler, we may ASSERT() or panic when moving a pCPU from Pool-0 to another cpupool. This is because pCPUs outside of any pool are considered part of cpupool0. This puts us at risk of crash when those same pCPUs are added to another pool and something different than the idle domain is found to be running on them. Note that, even if we prevent the above to happen (which is the purpose of this patch), this is still pretty bad, in fact, when we remove a pCPU from Pool-0: - in Credit1, as we do *not* update prv->ncpus and prv->credit, which means we're considering the wrong total credits when doing accounting; - in Credit2, the pCPU remains part of one runqueue, and is hence at least considered during load balancing, even if no vCPU should really run there. In Credit1, this "only" causes skewed accounting and no crashes because there is a lot of `cpumask_and`ing going on with the cpumask of the domains' cpupool (which, BTW, comes at a price). A quick and not to involved (and easily backportable) solution for Credit2, is to do exactly the same. Signed-off-by: Dario Faggioli Cc: Jan Beulich diff --git a/xen/common/sched_credit2.c b/xen/common/sched_credit2.c index 25b4c91..35dad15 100644 --- a/xen/common/sched_credit2.c +++ b/xen/common/sched_credit2.c @@ -331,19 +331,22 @@ static int csched2_cpu_pick(const struct scheduler *ops, struct vcpu *vc); */ static int get_fallback_cpu(struct csched2_vcpu *svc) { - int fallback_cpu, cpu = svc->vcpu->processor; + struct vcpu *v = svc->vcpu; + int cpu = v->processor; - if ( likely(cpumask_test_cpu(cpu, svc->vcpu->cpu_hard_affinity)) ) - return cpu; + cpumask_and(cpumask_scratch_cpu(cpu), v->cpu_hard_affinity, + cpupool_domain_cpumask(v->domain)); - cpumask_and(cpumask_scratch_cpu(cpu), svc->vcpu->cpu_hard_affinity, - &svc->rqd->active); - fallback_cpu = cpumask_first(cpumask_scratch_cpu(cpu)); - if ( likely(fallback_cpu < nr_cpu_ids) ) - return fallback_cpu; + if ( likely(cpumask_test_cpu(cpu, cpumask_scratch_cpu(cpu))) ) + return cpu; - cpumask_and(cpumask_scratch, svc->vcpu->cpu_hard_affinity, - cpupool_domain_cpumask(svc->vcpu->domain)); + if ( likely(cpumask_intersects(cpumask_scratch_cpu(cpu), + &svc->rqd->active)) ) + { + cpumask_and(cpumask_scratch_cpu(cpu), &svc->rqd->active, + cpumask_scratch_cpu(cpu)); + return cpumask_first(cpumask_scratch_cpu(cpu)); + } ASSERT(!cpumask_empty(cpumask_scratch_cpu(cpu))); @@ -582,9 +585,12 @@ runq_tickle(const struct scheduler *ops, unsigned int cpu, struct csched2_vcpu * goto tickle; } + cpumask_and(cpumask_scratch_cpu(cpu), new->vcpu->cpu_hard_affinity, + cpupool_domain_cpumask(new->vcpu->domain)); + /* Get a mask of idle, but not tickled, that new is allowed to run on. */ cpumask_andnot(&mask, &rqd->idle, &rqd->tickled); - cpumask_and(&mask, &mask, new->vcpu->cpu_hard_affinity); + cpumask_and(&mask, &mask, cpumask_scratch_cpu(cpu)); /* If it's not empty, choose one */ i = cpumask_cycle(cpu, &mask); @@ -599,7 +605,7 @@ runq_tickle(const struct scheduler *ops, unsigned int cpu, struct csched2_vcpu * * that new is allowed to run on. */ cpumask_andnot(&mask, &rqd->active, &rqd->idle); cpumask_andnot(&mask, &mask, &rqd->tickled); - cpumask_and(&mask, &mask, new->vcpu->cpu_hard_affinity); + cpumask_and(&mask, &mask, cpumask_scratch_cpu(cpu)); for_each_cpu(i, &mask) { @@ -1160,6 +1166,9 @@ choose_cpu(const struct scheduler *ops, struct vcpu *vc) return get_fallback_cpu(svc); } + cpumask_and(cpumask_scratch_cpu(cpu), vc->cpu_hard_affinity, + cpupool_domain_cpumask(vc->domain)); + /* First check to see if we're here because someone else suggested a place * for us to move. */ if ( test_and_clear_bit(__CSFLAG_runq_migrate_request, &svc->flags) ) @@ -1169,16 +1178,14 @@ choose_cpu(const struct scheduler *ops, struct vcpu *vc) printk("%s: Runqueue migrate aborted because target runqueue disappeared!\n", __func__); } - else + else if ( cpumask_intersects(cpumask_scratch_cpu(cpu), + &svc->migrate_rqd->active) ) { - cpumask_and(cpumask_scratch_cpu(cpu), vc->cpu_hard_affinity, + cpumask_and(cpumask_scratch_cpu(cpu), cpumask_scratch_cpu(cpu), &svc->migrate_rqd->active); new_cpu = cpumask_any(cpumask_scratch_cpu(cpu)); - if ( new_cpu < nr_cpu_ids ) - { - d2printk("%pv +\n", svc->vcpu); - goto out_up; - } + d2printk("%pv +\n", svc->vcpu); + goto out_up; } /* Fall-through to normal cpu pick */ } @@ -1208,12 +1215,12 @@ choose_cpu(const struct scheduler *ops, struct vcpu *vc) */ if ( rqd == svc->rqd ) { - if ( cpumask_intersects(vc->cpu_hard_affinity, &rqd->active) ) + if ( cpumask_intersects(cpumask_scratch_cpu(cpu), &rqd->active) ) rqd_avgload = rqd->b_avgload - svc->avgload; } else if ( spin_trylock(&rqd->lock) ) { - if ( cpumask_intersects(vc->cpu_hard_affinity, &rqd->active) ) + if ( cpumask_intersects(cpumask_scratch_cpu(cpu), &rqd->active) ) rqd_avgload = rqd->b_avgload; spin_unlock(&rqd->lock); @@ -1231,7 +1238,7 @@ choose_cpu(const struct scheduler *ops, struct vcpu *vc) new_cpu = get_fallback_cpu(svc); else { - cpumask_and(cpumask_scratch_cpu(cpu), vc->cpu_hard_affinity, + cpumask_and(cpumask_scratch_cpu(cpu), cpumask_scratch_cpu(cpu), &prv->rqd[min_rqi].active); new_cpu = cpumask_any(cpumask_scratch_cpu(cpu)); BUG_ON(new_cpu >= nr_cpu_ids); @@ -1318,6 +1325,8 @@ static void migrate(const struct scheduler *ops, __runq_deassign(svc); cpumask_and(cpumask_scratch_cpu(cpu), svc->vcpu->cpu_hard_affinity, + cpupool_domain_cpumask(svc->vcpu->domain)); + cpumask_and(cpumask_scratch_cpu(cpu), cpumask_scratch_cpu(cpu), &trqd->active); svc->vcpu->processor = cpumask_any(cpumask_scratch_cpu(cpu)); BUG_ON(svc->vcpu->processor >= nr_cpu_ids); @@ -1343,8 +1352,14 @@ static void migrate(const struct scheduler *ops, static bool_t vcpu_is_migrateable(struct csched2_vcpu *svc, struct csched2_runqueue_data *rqd) { + struct vcpu *v = svc->vcpu; + int cpu = svc->vcpu->processor; + + cpumask_and(cpumask_scratch_cpu(cpu), v->cpu_hard_affinity, + cpupool_domain_cpumask(v->domain)); + return !(svc->flags & CSFLAG_runq_migrate_request) && - cpumask_intersects(svc->vcpu->cpu_hard_affinity, &rqd->active); + cpumask_intersects(cpumask_scratch_cpu(cpu), &rqd->active); } static void balance_load(const struct scheduler *ops, int cpu, s_time_t now)