@@ -386,8 +386,6 @@ void __cpu_disable(void)
/* It's now safe to remove this processor from the online map */
cpumask_clear_cpu(cpu, &cpu_online_map);
- if ( cpu_disable_scheduler(cpu) )
- BUG();
smp_mb();
/* Return to caller; eventually the IPI mechanism will unwind and the
@@ -1221,9 +1221,6 @@ void __cpu_disable(void)
cpumask_clear_cpu(cpu, &cpu_online_map);
fixup_irqs(&cpu_online_map, 1);
fixup_eoi();
-
- if ( cpu_disable_scheduler(cpu) )
- BUG();
}
void __cpu_die(unsigned int cpu)
@@ -773,8 +773,9 @@ void restore_vcpu_affinity(struct domain *d)
}
/*
- * This function is used by cpu_hotplug code from stop_machine context
+ * This function is used by cpu_hotplug code via cpu notifier chain
* and from cpupools to switch schedulers on a cpu.
+ * Caller must get domlist_read_lock.
*/
int cpu_disable_scheduler(unsigned int cpu)
{
@@ -789,12 +790,6 @@ int cpu_disable_scheduler(unsigned int cpu)
if ( c == NULL )
return ret;
- /*
- * We'd need the domain RCU lock, but:
- * - when we are called from cpupool code, it's acquired there already;
- * - when we are called for CPU teardown, we're in stop-machine context,
- * so that's not be a problem.
- */
for_each_domain_in_cpupool ( d, c )
{
for_each_vcpu ( d, v )
@@ -893,6 +888,30 @@ int cpu_disable_scheduler(unsigned int cpu)
return ret;
}
+static int cpu_disable_scheduler_check(unsigned int cpu)
+{
+ struct domain *d;
+ struct vcpu *v;
+ struct cpupool *c;
+
+ c = per_cpu(cpupool, cpu);
+ if ( c == NULL )
+ return 0;
+
+ for_each_domain_in_cpupool ( d, c )
+ {
+ for_each_vcpu ( d, v )
+ {
+ if ( v->affinity_broken )
+ return -EADDRINUSE;
+ if ( system_state != SYS_STATE_suspend && v->processor == cpu )
+ return -EAGAIN;
+ }
+ }
+
+ return 0;
+}
+
/*
* In general, this must be called with the scheduler lock held, because the
* adjust_affinity hook may want to modify the vCPU state. However, when the
@@ -1734,7 +1753,16 @@ static int cpu_schedule_callback(
case CPU_UP_PREPARE:
rc = cpu_schedule_up(cpu);
break;
+ case CPU_DOWN_PREPARE:
+ rcu_read_lock(&domlist_read_lock);
+ rc = cpu_disable_scheduler_check(cpu);
+ rcu_read_unlock(&domlist_read_lock);
+ break;
case CPU_DEAD:
+ rcu_read_lock(&domlist_read_lock);
+ rc = cpu_disable_scheduler(cpu);
+ BUG_ON(rc);
+ rcu_read_unlock(&domlist_read_lock);
SCHED_OP(sched, deinit_pdata, sd->sched_priv, cpu);
/* Fallthrough */
case CPU_UP_CANCELED: