]> xenbits.xensource.com Git - people/andrewcoop/xen.git/commitdiff
x86/iommu: disable interrupts at shutdown
authorRoger Pau Monne <roger.pau@citrix.com>
Tue, 4 Feb 2025 10:46:14 +0000 (11:46 +0100)
committerRoger Pau Monne <roger.pau@citrix.com>
Wed, 12 Feb 2025 14:56:07 +0000 (15:56 +0100)
Add a new hook to inhibit interrupt generation by the IOMMU(s).  Note the
hook is currently only implemented for x86 IOMMUs.  The purpose is to
disable interrupt generation at shutdown so any kexec chained image finds
the IOMMU(s) in a quiesced state.

It would also prevent "Receive accept error" being raised as a result of
non-disabled interrupts targeting offline CPUs.

Note that the iommu_quiesce() call in nmi_shootdown_cpus() is still
required even when there's a preceding iommu_crash_shutdown() call; the
later can become a no-op depending on the setting of the "crash-disable"
command line option.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Release-Acked-by: Oleksii Kurochko<oleksii.kurochko@gmail.com>
xen/arch/x86/crash.c
xen/arch/x86/smp.c
xen/drivers/passthrough/amd/iommu.h
xen/drivers/passthrough/amd/iommu_init.c
xen/drivers/passthrough/amd/pci_amd_iommu.c
xen/drivers/passthrough/iommu.c
xen/drivers/passthrough/vtd/iommu.c
xen/include/xen/iommu.h

index 22b1121d7a4087b8978103ce301768e4dc101d83..26057c71d3c9bcb90caa4edcda61fe118f98297b 100644 (file)
@@ -187,6 +187,7 @@ static void nmi_shootdown_cpus(void)
 
         disable_IO_APIC();
         hpet_disable();
+        iommu_quiesce();
     }
 }
 
index 28bc041e03a951de8652c7e6a7cbc69c515903d0..516dab5528c1d7456d7133b377626d77203c90eb 100644 (file)
@@ -377,6 +377,7 @@ void smp_send_stop(void)
     pci_disable_msi_all();
     disable_IO_APIC();
     hpet_disable();
+    iommu_quiesce();
 
     if ( num_online_cpus() > 1 )
     {
index c32e9e9a1602fc0e13df6afdec97523bde78c9b0..00e81b4b2ab3f00f80d8985781db1bbcfc7c222a 100644 (file)
@@ -292,6 +292,7 @@ extern unsigned long *shared_intremap_inuse;
 void cf_check amd_iommu_resume(void);
 int __must_check cf_check amd_iommu_suspend(void);
 void cf_check amd_iommu_crash_shutdown(void);
+void cf_check amd_iommu_quiesce(void);
 
 static inline u32 get_field_from_reg_u32(u32 reg_value, u32 mask, u32 shift)
 {
index ed5e684b93dad816ca81519e51b7f19199a54f16..934adc5abe593ecaebec495d0951ba490832e27c 100644 (file)
@@ -1608,3 +1608,20 @@ void cf_check amd_iommu_resume(void)
         invalidate_all_domain_pages();
     }
 }
+
+void cf_check amd_iommu_quiesce(void)
+{
+    struct amd_iommu *iommu;
+
+    for_each_amd_iommu ( iommu )
+    {
+        if ( iommu->ctrl.int_cap_xt_en )
+        {
+            iommu->ctrl.int_cap_xt_en = false;
+            writeq(iommu->ctrl.raw,
+                   iommu->mmio_base + IOMMU_CONTROL_MMIO_OFFSET);
+        }
+        else
+            amd_iommu_msi_enable(iommu, IOMMU_CONTROL_DISABLED);
+    }
+}
index f96f59440bcc8faecd46f8267b5d25bff99f6ce5..d00697edb3a21abf6d9f99997ce643503475fe6a 100644 (file)
@@ -791,6 +791,7 @@ static const struct iommu_ops __initconst_cf_clobber _iommu_ops = {
     .crash_shutdown = amd_iommu_crash_shutdown,
     .get_reserved_device_memory = amd_iommu_get_reserved_device_memory,
     .dump_page_tables = amd_dump_page_tables,
+    .quiesce = amd_iommu_quiesce,
 };
 
 static const struct iommu_init_ops __initconstrel _iommu_init_ops = {
index 9e74a1fc72fa36cb2fd249a8d5e30b156a185028..16aad86973f90e3877b0877427cc5095adceae79 100644 (file)
@@ -663,6 +663,18 @@ void iommu_crash_shutdown(void)
 #endif
 }
 
+void iommu_quiesce(void)
+{
+    const struct iommu_ops *ops;
+
+    if ( !iommu_enabled )
+        return;
+
+    ops = iommu_get_ops();
+    if ( ops->quiesce )
+        iommu_vcall(ops, quiesce);
+}
+
 int iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt)
 {
     const struct iommu_ops *ops;
index 9d7a9977a6a6f3e7c0b54b97f679cd1df437f340..a1927d9f126d523a3c81452488dfa4cda22965b9 100644 (file)
@@ -3207,6 +3207,24 @@ static int cf_check intel_iommu_quarantine_init(struct pci_dev *pdev,
     return rc;
 }
 
+static void cf_check vtd_quiesce(void)
+{
+    const struct acpi_drhd_unit *drhd;
+
+    for_each_drhd_unit ( drhd )
+    {
+        const struct vtd_iommu *iommu = drhd->iommu;
+        uint32_t sts = dmar_readl(iommu->reg, DMAR_FECTL_REG);
+
+        /*
+         * Open code dma_msi_mask() to avoid taking the spinlock which could
+         * deadlock if called from crash context.
+         */
+        sts |= DMA_FECTL_IM;
+        dmar_writel(iommu->reg, DMAR_FECTL_REG, sts);
+    }
+}
+
 static const struct iommu_ops __initconst_cf_clobber vtd_ops = {
     .page_sizes = PAGE_SIZE_4K,
     .init = intel_iommu_domain_init,
@@ -3236,6 +3254,7 @@ static const struct iommu_ops __initconst_cf_clobber vtd_ops = {
     .iotlb_flush = iommu_flush_iotlb,
     .get_reserved_device_memory = intel_iommu_get_reserved_device_memory,
     .dump_page_tables = vtd_dump_page_tables,
+    .quiesce = vtd_quiesce,
 };
 
 const struct iommu_init_ops __initconstrel intel_iommu_init_ops = {
index b928c67e19957de712db7687363a1e71b35c6287..77a514019cc6169594b0167ddb4d30a73d643125 100644 (file)
@@ -314,6 +314,8 @@ struct iommu_ops {
      */
     int (*dt_xlate)(device_t *dev, const struct dt_phandle_args *args);
 #endif
+    /* Inhibit all interrupt generation, to be used at shutdown. */
+    void (*quiesce)(void);
 };
 
 /*
@@ -404,6 +406,7 @@ static inline int iommu_do_domctl(struct xen_domctl *domctl, struct domain *d,
 int __must_check iommu_suspend(void);
 void iommu_resume(void);
 void iommu_crash_shutdown(void);
+void iommu_quiesce(void);
 int iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt);
 int iommu_quarantine_dev_init(device_t *dev);