x86/apic: Add static key to Control IPI shorthands
authorThomas Gleixner <tglx@linutronix.de>
Mon, 22 Jul 2019 18:47:22 +0000 (20:47 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 25 Jul 2019 14:12:00 +0000 (16:12 +0200)
The IPI shorthand functionality delivers IPI/NMI broadcasts to all CPUs in
the system. This can have similar side effects as the MCE broadcasting when
CPUs are waiting in the BIOS or are offlined.

The kernel tracks already the state of offlined CPUs whether they have been
brought up at least once so that the CR4 MCE bit is set to make sure that
MCE broadcasts can't brick the machine.

Utilize that information and compare it to the cpu_present_mask. If all
present CPUs have been brought up at least once then the broadcast side
effect is mitigated by disabling regular interrupt/IPI delivery in the APIC
itself and by the cpu offline check at the begin of the NMI handler.

Use a static key to switch between broadcasting via shorthands or sending
the IPI/NMI one by one.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20190722105220.386410643@linutronix.de
arch/x86/include/asm/apic.h
arch/x86/kernel/apic/ipi.c
arch/x86/kernel/apic/local.h
arch/x86/kernel/cpu/common.c

index cae7e0d02476adc59dd971ff209b989e0aaf6e61..4a0d349ab44d867ed749f6057265133750c58f3f 100644 (file)
@@ -505,8 +505,10 @@ extern int default_check_phys_apicid_present(int phys_apicid);
 
 #ifdef CONFIG_SMP
 bool apic_id_is_primary_thread(unsigned int id);
+void apic_smt_update(void);
 #else
 static inline bool apic_id_is_primary_thread(unsigned int id) { return false; }
+static inline void apic_smt_update(void) { }
 #endif
 
 extern void irq_enter(void);
index ca3bcdb7c4a893b893b4d300b5bb0fb844830654..5bd8a001a887cc1efdff908023fae05ad6e15927 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "local.h"
 
+DEFINE_STATIC_KEY_FALSE(apic_use_ipi_shorthand);
+
 #ifdef CONFIG_SMP
 #ifdef CONFIG_HOTPLUG_CPU
 #define DEFAULT_SEND_IPI       (1)
@@ -28,7 +30,27 @@ static int __init print_ipi_mode(void)
        return 0;
 }
 late_initcall(print_ipi_mode);
-#endif
+
+void apic_smt_update(void)
+{
+       /*
+        * Do not switch to broadcast mode if:
+        * - Disabled on the command line
+        * - Only a single CPU is online
+        * - Not all present CPUs have been at least booted once
+        *
+        * The latter is important as the local APIC might be in some
+        * random state and a broadcast might cause havoc. That's
+        * especially true for NMI broadcasting.
+        */
+       if (apic_ipi_shorthand_off || num_online_cpus() == 1 ||
+           !cpumask_equal(cpu_present_mask, &cpus_booted_once_mask)) {
+               static_branch_disable(&apic_use_ipi_shorthand);
+       } else {
+               static_branch_enable(&apic_use_ipi_shorthand);
+       }
+}
+#endif /* CONFIG_SMP */
 
 static inline int __prepare_ICR2(unsigned int mask)
 {
index bd074e5997b07b203d97132628f39ef67ca2151c..391594cd5ca9fcb493648f318e2e3af4f6a17297 100644 (file)
@@ -7,6 +7,9 @@
  * (c) 1998-99, 2000 Ingo Molnar <mingo@redhat.com>
  * (c) 2002,2003 Andi Kleen, SuSE Labs.
  */
+
+#include <linux/jump_label.h>
+
 #include <asm/apic.h>
 
 /* APIC flat 64 */
@@ -22,6 +25,9 @@ int x2apic_phys_pkg_id(int initial_apicid, int index_msb);
 void x2apic_send_IPI_self(int vector);
 
 /* IPI */
+
+DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand);
+
 static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector,
                                         unsigned int dest)
 {
index 1ee6598c5d830477b9f9c40eea5add058a8f3aca..e0489d2860d30aa809cd87df6b274f9001448bd5 100644 (file)
@@ -1953,4 +1953,6 @@ void arch_smt_update(void)
 {
        /* Handle the speculative execution misfeatures */
        cpu_bugs_smt_update();
+       /* Check whether IPI broadcasting can be enabled */
+       apic_smt_update();
 }