Skip to content

Commit 10e6676

Browse files
vittyvkIngo Molnar
authored andcommitted
x86/smpboot: Unbreak CPU0 hotplug
A hang on CPU0 onlining after a preceding offlining is observed. Trace shows that CPU0 is stuck in check_tsc_sync_target() waiting for source CPU to run check_tsc_sync_source() but this never happens. Source CPU, in its turn, is stuck on synchronize_sched() which is called from native_cpu_up() -> do_boot_cpu() -> unregister_nmi_handler(). So it's a classic ABBA deadlock, due to the use of synchronize_sched() in unregister_nmi_handler(). Fix the bug by moving unregister_nmi_handler() from do_boot_cpu() to native_cpu_up() after cpu onlining is done. Signed-off-by: Vitaly Kuznetsov <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent e93c173 commit 10e6676

File tree

1 file changed

+17
-13
lines changed

1 file changed

+17
-13
lines changed

arch/x86/kernel/smpboot.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -971,15 +971,15 @@ void common_cpu_up(unsigned int cpu, struct task_struct *idle)
971971
* Returns zero if CPU booted OK, else error code from
972972
* ->wakeup_secondary_cpu.
973973
*/
974-
static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
974+
static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle,
975+
int *cpu0_nmi_registered)
975976
{
976977
volatile u32 *trampoline_status =
977978
(volatile u32 *) __va(real_mode_header->trampoline_status);
978979
/* start_ip had better be page-aligned! */
979980
unsigned long start_ip = real_mode_header->trampoline_start;
980981

981982
unsigned long boot_error = 0;
982-
int cpu0_nmi_registered = 0;
983983
unsigned long timeout;
984984

985985
idle->thread.sp = (unsigned long)task_pt_regs(idle);
@@ -1035,7 +1035,7 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
10351035
boot_error = apic->wakeup_secondary_cpu(apicid, start_ip);
10361036
else
10371037
boot_error = wakeup_cpu_via_init_nmi(cpu, start_ip, apicid,
1038-
&cpu0_nmi_registered);
1038+
cpu0_nmi_registered);
10391039

10401040
if (!boot_error) {
10411041
/*
@@ -1080,21 +1080,16 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
10801080
*/
10811081
smpboot_restore_warm_reset_vector();
10821082
}
1083-
/*
1084-
* Clean up the nmi handler. Do this after the callin and callout sync
1085-
* to avoid impact of possible long unregister time.
1086-
*/
1087-
if (cpu0_nmi_registered)
1088-
unregister_nmi_handler(NMI_LOCAL, "wake_cpu0");
10891083

10901084
return boot_error;
10911085
}
10921086

10931087
int native_cpu_up(unsigned int cpu, struct task_struct *tidle)
10941088
{
10951089
int apicid = apic->cpu_present_to_apicid(cpu);
1090+
int cpu0_nmi_registered = 0;
10961091
unsigned long flags;
1097-
int err;
1092+
int err, ret = 0;
10981093

10991094
WARN_ON(irqs_disabled());
11001095

@@ -1131,10 +1126,11 @@ int native_cpu_up(unsigned int cpu, struct task_struct *tidle)
11311126

11321127
common_cpu_up(cpu, tidle);
11331128

1134-
err = do_boot_cpu(apicid, cpu, tidle);
1129+
err = do_boot_cpu(apicid, cpu, tidle, &cpu0_nmi_registered);
11351130
if (err) {
11361131
pr_err("do_boot_cpu failed(%d) to wakeup CPU#%u\n", err, cpu);
1137-
return -EIO;
1132+
ret = -EIO;
1133+
goto unreg_nmi;
11381134
}
11391135

11401136
/*
@@ -1150,7 +1146,15 @@ int native_cpu_up(unsigned int cpu, struct task_struct *tidle)
11501146
touch_nmi_watchdog();
11511147
}
11521148

1153-
return 0;
1149+
unreg_nmi:
1150+
/*
1151+
* Clean up the nmi handler. Do this after the callin and callout sync
1152+
* to avoid impact of possible long unregister time.
1153+
*/
1154+
if (cpu0_nmi_registered)
1155+
unregister_nmi_handler(NMI_LOCAL, "wake_cpu0");
1156+
1157+
return ret;
11541158
}
11551159

11561160
/**

0 commit comments

Comments
 (0)