diff options
| author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-11-25 07:31:39 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-12-06 15:10:56 -0500 |
| commit | 6ad4c18884e864cf4c77f9074d3d1816063f99cd (patch) | |
| tree | f09643f6148b576fa2d23bf7d4b37d082d94e267 /kernel/cpu.c | |
| parent | e1b8090bdf125f8b2e192149547fead7f302a89c (diff) | |
sched: Fix balance vs hotplug race
Since (e761b77: cpu hotplug, sched: Introduce cpu_active_map and redo
sched domain managment) we have cpu_active_mask which is suppose to rule
scheduler migration and load-balancing, except it never (fully) did.
The particular problem being solved here is a crash in try_to_wake_up()
where select_task_rq() ends up selecting an offline cpu because
select_task_rq_fair() trusts the sched_domain tree to reflect the
current state of affairs, similarly select_task_rq_rt() trusts the
root_domain.
However, the sched_domains are updated from CPU_DEAD, which is after the
cpu is taken offline and after stop_machine is done. Therefore it can
race perfectly well with code assuming the domains are right.
Cure this by building the domains from cpu_active_mask on
CPU_DOWN_PREPARE.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/cpu.c')
| -rw-r--r-- | kernel/cpu.c | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 6ba0f1ecb212..b21688640377 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -212,6 +212,8 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) | |||
| 212 | err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod, | 212 | err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod, |
| 213 | hcpu, -1, &nr_calls); | 213 | hcpu, -1, &nr_calls); |
| 214 | if (err == NOTIFY_BAD) { | 214 | if (err == NOTIFY_BAD) { |
| 215 | set_cpu_active(cpu, true); | ||
| 216 | |||
| 215 | nr_calls--; | 217 | nr_calls--; |
| 216 | __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, | 218 | __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, |
| 217 | hcpu, nr_calls, NULL); | 219 | hcpu, nr_calls, NULL); |
| @@ -223,11 +225,11 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) | |||
| 223 | 225 | ||
| 224 | /* Ensure that we are not runnable on dying cpu */ | 226 | /* Ensure that we are not runnable on dying cpu */ |
| 225 | cpumask_copy(old_allowed, ¤t->cpus_allowed); | 227 | cpumask_copy(old_allowed, ¤t->cpus_allowed); |
| 226 | set_cpus_allowed_ptr(current, | 228 | set_cpus_allowed_ptr(current, cpu_active_mask); |
| 227 | cpumask_of(cpumask_any_but(cpu_online_mask, cpu))); | ||
| 228 | 229 | ||
| 229 | err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); | 230 | err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); |
| 230 | if (err) { | 231 | if (err) { |
| 232 | set_cpu_active(cpu, true); | ||
| 231 | /* CPU didn't die: tell everyone. Can't complain. */ | 233 | /* CPU didn't die: tell everyone. Can't complain. */ |
| 232 | if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, | 234 | if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, |
| 233 | hcpu) == NOTIFY_BAD) | 235 | hcpu) == NOTIFY_BAD) |
| @@ -292,9 +294,6 @@ int __ref cpu_down(unsigned int cpu) | |||
| 292 | 294 | ||
| 293 | err = _cpu_down(cpu, 0); | 295 | err = _cpu_down(cpu, 0); |
| 294 | 296 | ||
| 295 | if (cpu_online(cpu)) | ||
| 296 | set_cpu_active(cpu, true); | ||
| 297 | |||
| 298 | out: | 297 | out: |
| 299 | cpu_maps_update_done(); | 298 | cpu_maps_update_done(); |
| 300 | stop_machine_destroy(); | 299 | stop_machine_destroy(); |
| @@ -387,6 +386,15 @@ int disable_nonboot_cpus(void) | |||
| 387 | * with the userspace trying to use the CPU hotplug at the same time | 386 | * with the userspace trying to use the CPU hotplug at the same time |
| 388 | */ | 387 | */ |
| 389 | cpumask_clear(frozen_cpus); | 388 | cpumask_clear(frozen_cpus); |
| 389 | |||
| 390 | for_each_online_cpu(cpu) { | ||
| 391 | if (cpu == first_cpu) | ||
| 392 | continue; | ||
| 393 | set_cpu_active(cpu, false); | ||
| 394 | } | ||
| 395 | |||
| 396 | synchronize_sched(); | ||
| 397 | |||
| 390 | printk("Disabling non-boot CPUs ...\n"); | 398 | printk("Disabling non-boot CPUs ...\n"); |
| 391 | for_each_online_cpu(cpu) { | 399 | for_each_online_cpu(cpu) { |
| 392 | if (cpu == first_cpu) | 400 | if (cpu == first_cpu) |
