@@ -568,9 +568,23 @@ __runq_remove(struct csched2_vcpu *svc)
void burn_credits(struct csched2_runqueue_data *rqd, struct csched2_vcpu *, s_time_t);
-/* Check to see if the item on the runqueue is higher priority than what's
- * currently running; if so, wake up the processor */
-static /*inline*/ void
+/*
+ * Check what processor it is best to 'wake', for picking up a vcpu that has
+ * just been put (back) in the runqueue. Logic is as follows:
+ * 1. if there are idle processors in the runq, wake one of them;
+ * 2. if there aren't idle processor, check the one were the vcpu was
+ * running before to see if we can preempt what's running there now
+ * (and hence doing just one migration);
+ * 3. last stand: check all processors and see if the vcpu is in right
+ * of preempting any of the other vcpus running on them (this requires
+ * two migrations, and that's indeed why it is left as the last stand).
+ *
+ * Note that when we say 'idle processors' what we really mean is (pretty
+ * much always) both _idle_ and _not_already_tickled_. In fact, if a
+ * processor has been tickled, it will run csched2_schedule() shortly, and
+ * pick up some work, so it would be wrong to consider it idle.
+ */
+static void
runq_tickle(const struct scheduler *ops, struct csched2_vcpu *new, s_time_t now)
{
int i, ipid=-1;
@@ -584,22 +598,14 @@ runq_tickle(const struct scheduler *ops, struct csched2_vcpu *new, s_time_t now)
BUG_ON(new->rqd != rqd);
- /* Look at the cpu it's running on first */
- cur = CSCHED2_VCPU(curr_on_cpu(cpu));
- burn_credits(rqd, cur, now);
-
- if ( cur->credit < new->credit )
- {
- ipid = cpu;
- goto tickle;
- }
-
- /* Get a mask of idle, but not tickled, that new is allowed to run on. */
+ /*
+ * Get a mask of idle, but not tickled, processors that new is
+ * allowed to run on. If that's not empty, choose someone from there
+ * (preferrably, the one were new was running on already).
+ */
cpumask_andnot(&mask, &rqd->idle, &rqd->tickled);
cpumask_and(&mask, &mask, new->vcpu->cpu_hard_affinity);
-
- /* If it's not empty, choose one */
- i = cpumask_cycle(cpu, &mask);
+ i = cpumask_test_or_cycle(cpu, &mask);
if ( i < nr_cpu_ids )
{
SCHED_STAT_CRANK(tickled_idle_cpu);
@@ -607,12 +613,26 @@ runq_tickle(const struct scheduler *ops, struct csched2_vcpu *new, s_time_t now)
goto tickle;
}
- /* Otherwise, look for the non-idle cpu with the lowest credit,
- * skipping cpus which have been tickled but not scheduled yet,
- * that new is allowed to run on. */
+ /*
+ * Otherwise, look for the non-idle (and non-tickled) processors with
+ * the lowest credit, among the ones new is allowed to run on. Again,
+ * the cpu were it was running on would be the best candidate.
+ */
cpumask_andnot(&mask, &rqd->active, &rqd->idle);
cpumask_andnot(&mask, &mask, &rqd->tickled);
cpumask_and(&mask, &mask, new->vcpu->cpu_hard_affinity);
+ if ( cpumask_test_cpu(cpu, &mask) )
+ {
+ cur = CSCHED2_VCPU(curr_on_cpu(cpu));
+ burn_credits(rqd, cur, now);
+
+ if ( cur->credit < new->credit )
+ {
+ SCHED_STAT_CRANK(tickled_busy_cpu);
+ ipid = cpu;
+ goto tickle;
+ }
+ }
for_each_cpu(i, &mask)
{
@@ -624,7 +644,7 @@ runq_tickle(const struct scheduler *ops, struct csched2_vcpu *new, s_time_t now)
BUG_ON(is_idle_vcpu(cur->vcpu));
- /* Update credits for current to see if we want to preempt */
+ /* Update credits for current to see if we want to preempt. */
burn_credits(rqd, cur, now);
if ( cur->credit < lowest )
@@ -647,8 +667,10 @@ runq_tickle(const struct scheduler *ops, struct csched2_vcpu *new, s_time_t now)
}
}
- /* Only switch to another processor if the credit difference is greater
- * than the migrate resistance */
+ /*
+ * Only switch to another processor if the credit difference is
+ * greater than the migrate resistance.
+ */
if ( ipid == -1 || lowest + CSCHED2_MIGRATE_RESIST > new->credit )
{
SCHED_STAT_CRANK(tickled_no_cpu);
@@ -266,6 +266,14 @@ static inline int cpumask_cycle(int n, const cpumask_t *srcp)
return nxt;
}
+static inline int cpumask_test_or_cycle(int n, const cpumask_t *srcp)
+{
+ if ( cpumask_test_cpu(n, srcp) )
+ return n;
+
+ return cpumask_cycle(n, srcp);
+}
+
static inline unsigned int cpumask_any(const cpumask_t *srcp)
{
unsigned int cpu = cpumask_first(srcp);
If there are idle pCPUs, it's always better to try to "ship" the new vCPU there, instead than letting it preempting on a currently busy one. This commit also adds a cpumask_test_or_cycle() helper function, to make it easier to code the preference for the pCPU where the vCPU was running before. Signed-off-by: Dario Faggioli <dario.faggioli@citrix.com> --- Cc: George Dunlap <george.dunlap@citrix.com> Cc: Anshul Makkar <anshul.makkar@citrix.com> Cc: David Vrabel <david.vrabel@citrix.com> --- xen/common/sched_credit2.c | 68 +++++++++++++++++++++++++++++--------------- xen/include/xen/cpumask.h | 8 +++++ 2 files changed, 53 insertions(+), 23 deletions(-)