>From 5c2fb98c470102e4828e88080e64f8040c360d7c Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Mon, 2 Mar 2020 09:16:13 +0100 Subject: [PATCH] xen/sched: fix cpu offlining with core scheduling Offlining a cpu with core scheduling active can result in a hanging system. Reason is the scheduling resource and unit of the to be removed cpus needs to be split in order to remove the cpu from its cpupool and move it to the idle scheduler. In case one of the involved cpus happens to have received a sched slave event due to a vcpu former having been running on that cpu being woken up again, it can happen that this cpu will enter sched_wait_rendezvous_in() while its scheduling resource is just about to be split. It might wait for ever for the other sibling to join, which will never happen due to the resources already being modified. This can easily be avoided by: - resetting the rendezvous counters of the idle unit which is kept - checking for a new scheduling resource in sched_wait_rendezvous_in() after reacquiring the scheduling lock and resetting the counters in that case without scheduling another vcpu Reported-by: Igor Druzhinin Signed-off-by: Juergen Gross --- xen/common/sched/core.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c index 7e8e7d2c39..11a9589945 100644 --- a/xen/common/sched/core.c +++ b/xen/common/sched/core.c @@ -2415,7 +2415,8 @@ static struct sched_unit *sched_wait_rendezvous_in(struct sched_unit *prev, { struct sched_unit *next; struct vcpu *v; - unsigned int gran = get_sched_res(cpu)->granularity; + struct sched_resource *sr = get_sched_res(cpu); + unsigned int gran = sr->granularity; if ( !--prev->rendezvous_in_cnt ) { @@ -2482,6 +2483,19 @@ static struct sched_unit *sched_wait_rendezvous_in(struct sched_unit *prev, atomic_set(&prev->next_task->rendezvous_out_cnt, 0); prev->rendezvous_in_cnt = 0; } + + /* + * Check for scheduling resourced switched. This happens when we are + * moved away from our cpupool and cpus are subject of the idle + * scheduler now. + */ + if ( unlikely(sr != get_sched_res(cpu)) ) + { + ASSERT(is_idle_unit(prev)); + atomic_set(&prev->next_task->rendezvous_out_cnt, 0); + prev->rendezvous_in_cnt = 0; + return NULL; + } } return prev->next_task; @@ -2538,7 +2552,10 @@ static void sched_slave(void) next = sched_wait_rendezvous_in(prev, &lock, cpu, now); if ( !next ) + { + rcu_read_unlock(&sched_res_rculock); return; + } pcpu_schedule_unlock_irq(lock, cpu); @@ -2599,7 +2616,10 @@ static void schedule(void) cpumask_raise_softirq(mask, SCHED_SLAVE_SOFTIRQ); next = sched_wait_rendezvous_in(prev, &lock, cpu, now); if ( !next ) + { + rcu_read_unlock(&sched_res_rculock); return; + } } else { @@ -3151,7 +3171,10 @@ int schedule_cpu_rm(unsigned int cpu) per_cpu(sched_res_idx, cpu_iter) = 0; if ( cpu_iter == cpu ) { - idle_vcpu[cpu_iter]->sched_unit->priv = NULL; + unit = idle_vcpu[cpu_iter]->sched_unit; + unit->priv = NULL; + atomic_set(&unit->next_task->rendezvous_out_cnt, 0); + unit->rendezvous_in_cnt = 0; } else { -- 2.16.4