File 67acb687-x86-IOMMU-disable-IRQs-at-shutdown.patch of Package xen.37689
References: bsc#1233796
# Commit 819c3cb186a86ef3e04fb5af4d9f9f6de032c3ee
# Date 2025-02-12 15:56:07 +0100
# Author Roger Pau Monne <roger.pau@citrix.com>
# Committer Roger Pau Monne <roger.pau@citrix.com>
x86/iommu: disable interrupts at shutdown
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>
--- a/xen/arch/x86/crash.c
+++ b/xen/arch/x86/crash.c
@@ -187,6 +187,7 @@ static void nmi_shootdown_cpus(void)
disable_IO_APIC();
hpet_disable();
+ iommu_quiesce();
}
}
--- a/xen/arch/x86/smp.c
+++ b/xen/arch/x86/smp.c
@@ -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 )
{
--- a/xen/drivers/passthrough/amd/iommu.h
+++ b/xen/drivers/passthrough/amd/iommu.h
@@ -340,6 +340,7 @@ extern unsigned long *shared_intremap_in
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
--- a/xen/drivers/passthrough/amd/iommu_init.c
+++ b/xen/drivers/passthrough/amd/iommu_init.c
@@ -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);
+ }
+}
--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c
+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c
@@ -784,6 +784,7 @@ static const struct iommu_ops __initcons
.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 = {
--- a/xen/drivers/passthrough/iommu.c
+++ b/xen/drivers/passthrough/iommu.c
@@ -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;
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -3241,6 +3241,24 @@ static int cf_check intel_iommu_quaranti
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 __initcons
.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 = {
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -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
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);