[PATCH v5 7/7] PCI/MSI: re-enable conditional parent mask/unmask with sw moderation
From: Luigi Rizzo
Date: Wed Aug 19 2026 - 08:45:44 EST
Recent commits removed masking of interrupts at the PCIe level when
the masking is also done at the parent (e.g. on ARM with GIC). This
optimization saves the PCIe write, and especially the expensive readback,
but causes the device to still send MSI interrupts that are ignored.
As a result, it very harmful on platforms (many, for multiple
architectures) that are unable to deal with too many MSI interrupts, and
defeats GSIM (CONFIG_IRQ_SW_MODERATION), which is esplicitely designed to
address this problem, but depends on masking interrupts at the PCIe level.
Revert to the old behavior when GSIM (CONFIG_IRQ_SW_MODERATION)
is compiled in.
Signed-off-by: Luigi Rizzo <lrizzo@xxxxxxxxxx>
---
drivers/irqchip/irq-msi-lib.c | 8 ++++++++
drivers/pci/msi/irqdomain.c | 20 ++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index 45e0ed3134ce1..453a5d5a2c106 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -116,6 +116,14 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
if (!chip->irq_set_affinity && !(info->flags & MSI_FLAG_NO_AFFINITY))
chip->irq_set_affinity = msi_domain_set_affinity;
+ /*
+ * The problem addressed by software moderation depends on actually
+ * masking at the PCI level. irq_mask() can be optimized separately
+ * for the common cases outside the move/teardown paths.
+ */
+ if (IS_ENABLED(CONFIG_IRQ_SW_MODERATION))
+ return true;
+
/*
* If the parent domain insists on being in charge of masking, obey
* blindly. The interrupt is un-masked at the PCI level on startup
diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index 6e65f0f44112e..44b022fee7f1a 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -80,6 +80,22 @@ static unsigned int cond_startup_parent(struct irq_data *data)
return 0;
}
+static __always_inline void cond_mask_parent(struct irq_data *data)
+{
+ struct msi_domain_info *info = data->domain->host_data;
+
+ if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
+ irq_chip_mask_parent(data);
+}
+
+static __always_inline void cond_unmask_parent(struct irq_data *data)
+{
+ struct msi_domain_info *info = data->domain->host_data;
+
+ if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT))
+ irq_chip_unmask_parent(data);
+}
+
static void pci_irq_shutdown_msi(struct irq_data *data)
{
struct msi_desc *desc = irq_data_get_msi_desc(data);
@@ -102,12 +118,14 @@ static void pci_irq_mask_msi(struct irq_data *data)
struct msi_desc *desc = irq_data_get_msi_desc(data);
pci_msi_mask(desc, BIT(data->irq - desc->irq));
+ cond_mask_parent(data);
}
static void pci_irq_unmask_msi(struct irq_data *data)
{
struct msi_desc *desc = irq_data_get_msi_desc(data);
+ cond_unmask_parent(data);
pci_msi_unmask(desc, BIT(data->irq - desc->irq));
}
@@ -160,10 +178,12 @@ static unsigned int pci_irq_startup_msix(struct irq_data *data)
static void pci_irq_mask_msix(struct irq_data *data)
{
pci_msix_mask(irq_data_get_msi_desc(data));
+ cond_mask_parent(data);
}
static void pci_irq_unmask_msix(struct irq_data *data)
{
+ cond_unmask_parent(data);
pci_msix_unmask(irq_data_get_msi_desc(data));
}
--
2.55.0.737.g08866a6d13-goog