]> xenbits.xensource.com Git - xen.git/commitdiff
x86/iommu: disable interrupts at shutdown
authorRoger Pau Monné <roger.pau@citrix.com>
Mon, 17 Feb 2025 12:33:12 +0000 (13:33 +0100)
committerJan Beulich <jbeulich@suse.com>
Mon, 17 Feb 2025 12:33:12 +0000 (13:33 +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>
master commit: 819c3cb186a86ef3e04fb5af4d9f9f6de032c3ee
master date: 2025-02-12 15:56:07 +0100

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 3b79436b31c9eaf25218c3fbd731a3aa55e39045..1f86ed4f4ce6bdc7037b7431d1ad7886fe7333aa 100644 (file)
@@ -375,6 +375,7 @@ void smp_send_stop(void)
     pci_disable_msi_all();
     disable_IO_APIC();
     hpet_disable();
+    iommu_quiesce();
 
     if ( num_online_cpus() > 1 )
     {
index d4416ebc43896caedd4ea5b5405e1a28234e7080..551bc6c10cfae52efde9260f47e3ff581b694406 100644 (file)
@@ -340,6 +340,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);
 
 /* guest iommu support */
 #ifdef CONFIG_HVM
index d672e682f8546c4da297fa2df86eaeae1da9f12a..1388f112d5d70592490b6e08c6f1e4af0120f1d7 100644 (file)
@@ -1606,3 +1606,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 40a56008aa8c9d3fe30868dbb81d4f84eeb3d453..37ba23ac2d0533e53ec732171ef2f65aa3a63a9d 100644 (file)
@@ -784,6 +784,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 f9a9f53dbd448ee9b67547c64a4491621e7df528..46874d6c56151c54abc8340b29b5513841a64e12 100644 (file)
@@ -658,6 +658,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 a6f0071fe98cbe2b7f0263e2972f0c098326c144..5f9906a290ebbcd377598bc9b306515b99a0c2e7 100644 (file)
@@ -3241,6 +3241,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,
@@ -3270,6 +3288,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 0e747b0bbc1c334537666770a6f99ee9de596b37..e9296e36ee59259c1ccd3dfed7b2a1ff158ae7fc 100644 (file)
@@ -320,6 +320,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);
 };
 
 /*
@@ -400,6 +402,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);