[v2 06/13] x86/ipi: Support sending NMI_VECTOR as self ipi

From: Ashok Raj
Date: Thu Nov 03 2022 - 14:00:44 EST


From: Jacob Pan <jacob.jun.pan@xxxxxxxxxxxxxxx>

apic->send_IPI_self() can be used to send any general vector as a self IPI.
The function uses a SHORTCUT specifier, but it can't be used to send a
vector with delivery mode as NMI. Chapter 10, Advanved Programmable
Interrupt Controller (APIC) Table 10-3 indicates that the shortcut isn't a
legal combination for NMI delivery. The same is true for x2apic
implementations as well, the self IPI MSR can only specify the vector
number, but no delivery mode.

The helper adds proper handling if the vector is NMI_VECTOR.

Suggested-by: Ashok Raj <ashok.raj@xxxxxxxxx>
Signed-off-by: Jacob Pan <jacob.jun.pan@xxxxxxxxxxxxxxx>
Co-developed-by: Ashok Raj <ashok.raj@xxxxxxxxx>
Reviewed-by: Tony Luck <tony.luck@xxxxxxxxx>
Signed-off-by: Ashok Raj <ashok.raj@xxxxxxxxx>
---
arch/x86/kernel/apic/ipi.c | 6 +++++-
arch/x86/kernel/apic/x2apic_phys.c | 6 +++++-
arch/x86/kernel/nmi_selftest.c | 32 ++++++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c
index 2a6509e8c840..e967c49609ef 100644
--- a/arch/x86/kernel/apic/ipi.c
+++ b/arch/x86/kernel/apic/ipi.c
@@ -239,7 +239,11 @@ void default_send_IPI_all(int vector)

void default_send_IPI_self(int vector)
{
- __default_send_IPI_shortcut(APIC_DEST_SELF, vector);
+ if (unlikely(vector == NMI_VECTOR))
+ apic->send_IPI_mask(cpumask_of(smp_processor_id()),
+ NMI_VECTOR);
+ else
+ __default_send_IPI_shortcut(APIC_DEST_SELF, vector);
}

#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c
index 6bde05a86b4e..cf187f1906b2 100644
--- a/arch/x86/kernel/apic/x2apic_phys.c
+++ b/arch/x86/kernel/apic/x2apic_phys.c
@@ -149,7 +149,11 @@ int x2apic_phys_pkg_id(int initial_apicid, int index_msb)

void x2apic_send_IPI_self(int vector)
{
- apic_write(APIC_SELF_IPI, vector);
+ if (unlikely(vector == NMI_VECTOR))
+ apic->send_IPI_mask(cpumask_of(smp_processor_id()),
+ NMI_VECTOR);
+ else
+ apic_write(APIC_SELF_IPI, vector);
}

static struct apic apic_x2apic_phys __ro_after_init = {
diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c
index a1a96df3dff1..f4b813821208 100644
--- a/arch/x86/kernel/nmi_selftest.c
+++ b/arch/x86/kernel/nmi_selftest.c
@@ -105,6 +105,36 @@ static void __init local_ipi(void)
test_nmi_ipi(to_cpumask(nmi_ipi_mask));
}

+static void __init self_nmi_test(void)
+{
+ unsigned long timeout;
+
+ cpumask_clear(to_cpumask(nmi_ipi_mask));
+ cpumask_set_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask));
+
+ if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback,
+ NMI_FLAG_FIRST, "nmi_selftest", __initdata)) {
+ nmi_fail = FAILURE;
+ return;
+ }
+
+ /* sync above data before sending NMI */
+ wmb();
+
+ apic->send_IPI_self(NMI_VECTOR);
+
+ /* Don't wait longer than a second */
+ timeout = USEC_PER_SEC;
+ while (!cpumask_empty(to_cpumask(nmi_ipi_mask)) && --timeout)
+ udelay(1);
+
+ /* What happens if we timeout, do we still unregister?? */
+ unregister_nmi_handler(NMI_LOCAL, "nmi_selftest");
+
+ if (!timeout)
+ nmi_fail = TIMEOUT;
+}
+
static void __init reset_nmi(void)
{
nmi_fail = 0;
@@ -157,6 +187,8 @@ void __init nmi_selftest(void)
print_testname("local IPI");
dotest(local_ipi, SUCCESS);
printk(KERN_CONT "\n");
+ print_testname("Self NMI IPI");
+ dotest(self_nmi_test, SUCCESS);

cleanup_nmi_testsuite();

--
2.34.1