]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
x86/apic: Support 15 bits of APIC ID in MSI where available
authorDavid Woodhouse <dwmw@amazon.co.uk>
Sat, 24 Oct 2020 21:35:32 +0000 (22:35 +0100)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 29 Oct 2020 08:15:17 +0000 (08:15 +0000)
Some hypervisors can allow the guest to use the Extended Destination ID
field in the MSI address to address up to 32768 CPUs.

This applies to all downstream devices which generate MSI cycles,
including HPET, I/O-APIC and PCI MSI.

HPET and PCI MSI use the same __irq_msi_compose_msg() function, while
I/O-APIC generates its own and had support for the extended bits added in
a previous commit.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20201024213535.443185-33-dwmw2@infradead.org
(cherry picked from commit ab0f59c6f135289c7ea90b0e2471674bf289d884)

arch/x86/include/asm/mpspec.h
arch/x86/include/asm/x86_init.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/msi.c
arch/x86/kernel/x86_init.c

index 606cbaebd33661516745893f582b56b12f21e88a..089fb4df427e76937711bfa9d2ea8548d999c95e 100644 (file)
@@ -44,6 +44,7 @@ extern u8 boot_cpu_apic_version;
 extern unsigned long mp_lapic_addr;
 
 #ifdef CONFIG_X86_LOCAL_APIC
+extern bool virt_ext_dest_id;
 extern int smp_found_config;
 #else
 # define smp_found_config 0
index 6807153c04105ddc2a53dd69562308776fc74000..c8968ba0364fbd1b3ea9a7f99400e3ba2200fe76 100644 (file)
@@ -121,6 +121,7 @@ struct x86_init_pci {
  * @init_platform:             platform setup
  * @guest_late_init:           guest late init
  * @x2apic_available:          X2APIC detection
+ * @msi_ext_dest_id:           MSI supports 15-bit APIC IDs
  * @init_mem_mapping:          setup early mappings during init_mem_mapping()
  * @init_after_bootmem:                guest init after boot allocator is finished
  */
@@ -128,6 +129,7 @@ struct x86_hyper_init {
        void (*init_platform)(void);
        void (*guest_late_init)(void);
        bool (*x2apic_available)(void);
+       bool (*msi_ext_dest_id)(void);
        void (*init_mem_mapping)(void);
        void (*init_after_bootmem)(void);
 };
index 8ce92ca4100d14c8d10fa4a7b8085d20b161dae6..d427e0b997fb0f998cd93850b422e4c1727edf6c 100644 (file)
@@ -93,6 +93,11 @@ static unsigned int disabled_cpu_apicid __ro_after_init = BAD_APICID;
  */
 static int apic_extnmi __ro_after_init = APIC_EXTNMI_BSP;
 
+/*
+ * Hypervisor supports 15 bits of APIC ID in MSI Extended Destination ID
+ */
+bool virt_ext_dest_id __ro_after_init;
+
 /*
  * Map cpu index to physical APIC ID
  */
@@ -1838,6 +1843,8 @@ static __init void try_to_enable_x2apic(int remap_mode)
                return;
 
        if (remap_mode != IRQ_REMAP_X2APIC_MODE) {
+               u32 apic_limit = 255;
+
                /*
                 * Using X2APIC without IR is not architecturally supported
                 * on bare metal but may be supported in guests.
@@ -1848,12 +1855,22 @@ static __init void try_to_enable_x2apic(int remap_mode)
                        return;
                }
 
+               /*
+                * If the hypervisor supports extended destination ID in
+                * MSI, that increases the maximum APIC ID that can be
+                * used for non-remapped IRQ domains.
+                */
+               if (x86_init.hyper.msi_ext_dest_id()) {
+                       virt_ext_dest_id = 1;
+                       apic_limit = 32767;
+               }
+
                /*
                 * Without IR, all CPUs can be addressed by IOAPIC/MSI only
                 * in physical mode, and CPUs with an APIC ID that cannnot
                 * be addressed must not be brought online.
                 */
-               x2apic_set_max_apicid(255);
+               x2apic_set_max_apicid(apic_limit);
                x2apic_phys = 1;
        }
        x2apic_enable();
index cc2f5acfd8e728401c556e3195cc1a9161a59b50..857b66f4ed4242a11abb6d93ce5af5465652ee01 100644 (file)
@@ -46,10 +46,15 @@ static void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg,
         * Only the IOMMU itself can use the trick of putting destination
         * APIC ID into the high bits of the address. Anything else would
         * just be writing to memory if it tried that, and needs IR to
-        * address higher APIC IDs.
+        * address APICs which can't be addressed in the normal 32-bit
+        * address range at 0xFFExxxxx. That is typically just 8 bits, but
+        * some hypervisors allow the extended destination ID field in bits
+        * 11-5 to be used, giving support for 15 bits of APIC IDs in total.
         */
        if (dmar)
                msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid);
+       else if (virt_ext_dest_id && cfg->dest_apicid < 0x8000)
+               msg->address_lo |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid) >> 3;
        else
                WARN_ON_ONCE(MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid));
 }
index 123f1c1f1788e4749109648346a074f816abe687..2410dce4aeb124de108c3212c17b33b8ddf90243 100644 (file)
@@ -112,6 +112,7 @@ struct x86_init_ops x86_init __initdata = {
                .init_platform          = x86_init_noop,
                .guest_late_init        = x86_init_noop,
                .x2apic_available       = bool_x86_init_noop,
+               .msi_ext_dest_id        = bool_x86_init_noop,
                .init_mem_mapping       = x86_init_noop,
                .init_after_bootmem     = x86_init_noop,
        },