]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/apic: Provide apic_update_callback()
authorThomas Gleixner <tglx@linutronix.de>
Tue, 8 Aug 2023 22:04:19 +0000 (15:04 -0700)
committerDave Hansen <dave.hansen@linux.intel.com>
Wed, 9 Aug 2023 19:00:46 +0000 (12:00 -0700)
There are already two variants of update mechanism for particular callbacks
and virtualization just writes into the data structure.

Provide an interface and use a shadow data structure to preserve callbacks
so they can be reapplied when the APIC driver is replaced.

The extra data structure is intentional as any new callback needs to be
also updated in the core code. This also prepares for static calls.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Michael Kelley <mikelley@microsoft.com>
Tested-by: Sohil Mehta <sohil.mehta@intel.com>
Tested-by: Juergen Gross <jgross@suse.com> # Xen PV (dom0 and unpriv. guest)
arch/x86/include/asm/apic.h
arch/x86/kernel/apic/init.c
arch/x86/kernel/setup.c

index 90814ec845405f69c310b0d7851c58364ec0379f..6e279c1b380d8b904c61ba063d6c28c2a192b0bb 100644 (file)
@@ -308,6 +308,23 @@ struct apic {
        char    *name;
 };
 
+struct apic_override {
+       void    (*eoi)(void);
+       void    (*native_eoi)(void);
+       void    (*write)(u32 reg, u32 v);
+       u32     (*read)(u32 reg);
+       void    (*send_IPI)(int cpu, int vector);
+       void    (*send_IPI_mask)(const struct cpumask *mask, int vector);
+       void    (*send_IPI_mask_allbutself)(const struct cpumask *msk, int vec);
+       void    (*send_IPI_allbutself)(int vector);
+       void    (*send_IPI_all)(int vector);
+       void    (*send_IPI_self)(int vector);
+       u64     (*icr_read)(void);
+       void    (*icr_write)(u32 low, u32 high);
+       int     (*wakeup_secondary_cpu)(int apicid, unsigned long start_eip);
+       int     (*wakeup_secondary_cpu_64)(int apicid, unsigned long start_eip);
+};
+
 /*
  * Pointer to the local APIC driver in use on this system (there's
  * always just one such driver in use - the kernel decides via an
@@ -343,9 +360,17 @@ extern int lapic_can_unplug_cpu(void);
 #endif
 
 #ifdef CONFIG_X86_LOCAL_APIC
+extern struct apic_override __x86_apic_override;
 
+void __init apic_setup_apic_calls(void);
 void __init apic_install_driver(struct apic *driver);
 
+#define apic_update_callback(_callback, _fn) {                                 \
+               __x86_apic_override._callback = _fn;                            \
+               apic->_callback = _fn;                                          \
+               pr_info("APIC: %s() replaced with %ps()\n", #_callback, _fn);   \
+}
+
 static inline u32 apic_read(u32 reg)
 {
        return apic->read(reg);
@@ -405,6 +430,9 @@ static inline void apic_wait_icr_idle(void) { }
 static inline u32 safe_apic_wait_icr_idle(void) { return 0; }
 static inline void apic_set_eoi_cb(void (*eoi)(void)) {}
 static inline void apic_native_eoi(void) { WARN_ON_ONCE(1); }
+static inline void apic_setup_apic_calls(void) { }
+
+#define apic_update_callback(_callback, _fn) do { } while (0)
 
 #endif /* CONFIG_X86_LOCAL_APIC */
 
index 25cf39b855db2af73b6888375ad4dbf2b2595691..dab3afa8a86a410095ce448a740bea2c42eed22b 100644 (file)
@@ -5,6 +5,37 @@
 
 #include "local.h"
 
+/* The container for function call overrides */
+struct apic_override __x86_apic_override __initdata;
+
+#define apply_override(__cb)                                   \
+       if (__x86_apic_override.__cb)                           \
+               apic->__cb = __x86_apic_override.__cb
+
+static __init void restore_override_callbacks(void)
+{
+       apply_override(eoi);
+       apply_override(native_eoi);
+       apply_override(write);
+       apply_override(read);
+       apply_override(send_IPI);
+       apply_override(send_IPI_mask);
+       apply_override(send_IPI_mask_allbutself);
+       apply_override(send_IPI_allbutself);
+       apply_override(send_IPI_all);
+       apply_override(send_IPI_self);
+       apply_override(icr_read);
+       apply_override(icr_write);
+       apply_override(wakeup_secondary_cpu);
+       apply_override(wakeup_secondary_cpu_64);
+}
+
+void __init apic_setup_apic_calls(void)
+{
+       /* Ensure that the default APIC has native_eoi populated */
+       apic->native_eoi = apic->eoi;
+}
+
 void __init apic_install_driver(struct apic *driver)
 {
        if (apic == driver)
@@ -15,6 +46,13 @@ void __init apic_install_driver(struct apic *driver)
        if (IS_ENABLED(CONFIG_X86_X2APIC) && apic->x2apic_set_max_apicid)
                apic->max_apic_id = x2apic_max_apicid;
 
+       /* Copy the original eoi() callback as KVM/HyperV might overwrite it */
+       if (!apic->native_eoi)
+               apic->native_eoi = apic->eoi;
+
+       /* Apply any already installed callback overrides */
+       restore_override_callbacks();
+
        pr_info("Switched APIC routing to: %s\n", driver->name);
 }
 
@@ -41,7 +79,6 @@ void __init apic_set_eoi_cb(void (*eoi)(void))
        for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
                /* Should happen once for each apic */
                WARN_ON((*drv)->eoi == eoi);
-               (*drv)->native_eoi = (*drv)->eoi;
                (*drv)->eoi = eoi;
        }
 }
index f83d02b4fb258518e26d168d66b5542286b7b761..b9145a63da77d83586eb6fe608bfa0ff617ddd9b 100644 (file)
@@ -1017,6 +1017,8 @@ void __init setup_arch(char **cmdline_p)
 
        x86_report_nx();
 
+       apic_setup_apic_calls();
+
        if (acpi_mps_check()) {
 #ifdef CONFIG_X86_LOCAL_APIC
                apic_is_disabled = true;