Skip to content

Commit cb81dee

Browse files
KAGA-KOKObp3tk0v
authored andcommitted
x86/idle: Sanitize X86_BUG_AMD_E400 handling
amd_e400_idle(), the idle routine for AMD CPUs which are affected by erratum 400 violates the RCU constraints by invoking tick_broadcast_enter() and tick_broadcast_exit() after the core code has marked RCU non-idle. The functions can end up in lockdep or tracing, which rightfully triggers a RCU warning. The core code provides now a static branch conditional invocation of the broadcast functions. Remove amd_e400_idle(), enforce default_idle() and enable the static branch on affected CPUs to cure this. [ bp: Fold in a fix for a IS_ENABLED() check fail missing a "CONFIG_" prefix which tglx spotted. ] Reported-by: Borislav Petkov <bp@alien8.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/877cim6sis.ffs@tglx
1 parent 2be2a19 commit cb81dee

File tree

2 files changed

+10
-33
lines changed

2 files changed

+10
-33
lines changed

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ config X86
147147
select EDAC_ATOMIC_SCRUB
148148
select EDAC_SUPPORT
149149
select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC)
150+
select GENERIC_CLOCKEVENTS_BROADCAST_IDLE if GENERIC_CLOCKEVENTS_BROADCAST
150151
select GENERIC_CLOCKEVENTS_MIN_ADJUST
151152
select GENERIC_CMOS_UPDATE
152153
select GENERIC_CPU_AUTOPROBE

arch/x86/kernel/process.c

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -845,31 +845,6 @@ void __noreturn stop_this_cpu(void *dummy)
845845
}
846846
}
847847

848-
/*
849-
* AMD Erratum 400 aware idle routine. We handle it the same way as C3 power
850-
* states (local apic timer and TSC stop).
851-
*
852-
* XXX this function is completely buggered vs RCU and tracing.
853-
*/
854-
static void amd_e400_idle(void)
855-
{
856-
/*
857-
* We cannot use static_cpu_has_bug() here because X86_BUG_AMD_APIC_C1E
858-
* gets set after static_cpu_has() places have been converted via
859-
* alternatives.
860-
*/
861-
if (!boot_cpu_has_bug(X86_BUG_AMD_APIC_C1E)) {
862-
default_idle();
863-
return;
864-
}
865-
866-
tick_broadcast_enter();
867-
868-
default_idle();
869-
870-
tick_broadcast_exit();
871-
}
872-
873848
/*
874849
* Prefer MWAIT over HALT if MWAIT is supported, MWAIT_CPUID leaf
875850
* exists and whenever MONITOR/MWAIT extensions are present there is at
@@ -890,8 +865,8 @@ static int prefer_mwait_c1_over_halt(const struct cpuinfo_x86 *c)
890865
if (!cpu_has(c, X86_FEATURE_MWAIT))
891866
return 0;
892867

893-
/* Monitor has a bug. Fallback to HALT */
894-
if (boot_cpu_has_bug(X86_BUG_MONITOR))
868+
/* Monitor has a bug or APIC stops in C1E. Fallback to HALT */
869+
if (boot_cpu_has_bug(X86_BUG_MONITOR) || boot_cpu_has_bug(X86_BUG_AMD_APIC_C1E))
895870
return 0;
896871

897872
cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
@@ -942,17 +917,15 @@ void select_idle_routine(const struct cpuinfo_x86 *c)
942917
if (x86_idle_set() || boot_option_idle_override == IDLE_POLL)
943918
return;
944919

945-
if (boot_cpu_has_bug(X86_BUG_AMD_E400)) {
946-
pr_info("using AMD E400 aware idle routine\n");
947-
static_call_update(x86_idle, amd_e400_idle);
948-
} else if (prefer_mwait_c1_over_halt(c)) {
920+
if (prefer_mwait_c1_over_halt(c)) {
949921
pr_info("using mwait in idle threads\n");
950922
static_call_update(x86_idle, mwait_idle);
951923
} else if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) {
952924
pr_info("using TDX aware idle routine\n");
953925
static_call_update(x86_idle, tdx_safe_halt);
954-
} else
926+
} else {
955927
static_call_update(x86_idle, default_idle);
928+
}
956929
}
957930

958931
void amd_e400_c1e_apic_setup(void)
@@ -985,7 +958,10 @@ void __init arch_post_acpi_subsys_init(void)
985958

986959
if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
987960
mark_tsc_unstable("TSC halt in AMD C1E");
988-
pr_info("System has AMD C1E enabled\n");
961+
962+
if (IS_ENABLED(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST_IDLE))
963+
static_branch_enable(&arch_needs_tick_broadcast);
964+
pr_info("System has AMD C1E erratum E400. Workaround enabled.\n");
989965
}
990966

991967
static int __init idle_setup(char *str)

0 commit comments

Comments
 (0)