[SRU][R][PATCH 2/2] UBUNTU: SAUCE: PCI: vmd: Work around ARL004 on 8086:ad0b

Vladimir Nedoshivin gvozd188 at mail.ru
Tue Sep 8 22:00:47 UTC 2026


BugLink: https://bugs.launchpad.net/bugs/2166325

Intel Arrow Lake VMD device 8086:ad0b is affected by ARL004. A posted
write from a device behind VMD may not be globally visible when the MSI
is observed by the VMD interrupt handler.

For NVMe devices behind 8086:ad0b, retain a configuration-space address
for the MSI initiator and issue a dummy read before dispatching the
interrupt. The read flushes outstanding posted writes before the NVMe
completion interrupt is handled.

Limit the workaround to VMD device 8086:ad0b and NVMe endpoints.

The workaround has been tested on the affected Arrow Lake system with
Ubuntu 7.0.0-31-generic.

Signed-off-by: Vladimir Nedoshivin <gvozd188 at mail.ru>
---
 drivers/pci/controller/vmd.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 59fe330..ae4f8c1 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -194,6 +194,9 @@ static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
 	return container_of(bus->sysdata, struct vmd_dev, sysdata);
 }
 
+static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
+				 unsigned int devfn, int reg, int len);
+
 static inline unsigned int index_from_irqs(struct vmd_dev *vmd,
 					   struct vmd_irq_list *irqs)
 {
@@ -312,6 +315,7 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq,
 {
 	struct msi_desc *desc = ((msi_alloc_info_t *)arg)->desc;
 	struct vmd_dev *vmd = domain->host_data;
+	struct pci_dev *pdev = msi_desc_to_pci_dev(desc);
 	struct vmd_irq *vmdirq;
 
 	for (int i = 0; i < nr_irqs; ++i) {
@@ -325,6 +329,11 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq,
 		vmdirq->irq = vmd_next_irq(vmd, desc);
 		vmdirq->virq = virq + i;
 
+		if (vmd->dev->device == 0xad0b &&
+		    pdev->class == PCI_CLASS_STORAGE_EXPRESS)
+			vmdirq->flush_addr = vmd_cfg_addr(vmd, pdev->bus,
+						 pdev->devfn, PCI_DEVICE_ID, 2);
+
 		irq_domain_set_info(domain, virq + i, vmdirq->irq->virq,
 				    &vmd_msi_controller, vmdirq,
 				    handle_untracked_irq, vmd, NULL);
@@ -754,11 +763,18 @@ static irqreturn_t vmd_irq(int irq, void *data)
 {
 	struct vmd_irq_list *irqs = data;
 	struct vmd_irq *vmdirq;
+	unsigned long flags;
 	int idx;
 
 	idx = srcu_read_lock(&irqs->srcu);
-	list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
+	list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) {
+		if (vmdirq->flush_addr) {
+			raw_spin_lock_irqsave(&irqs->vmd->cfg_lock, flags);
+			readw(vmdirq->flush_addr);
+			raw_spin_unlock_irqrestore(&irqs->vmd->cfg_lock, flags);
+		}
 		generic_handle_irq(vmdirq->virq);
+	}
 	srcu_read_unlock(&irqs->srcu, idx);
 
 	return IRQ_HANDLED;
@@ -789,6 +805,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
 			return err;
 
 		INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
+		vmd->irqs[i].vmd = vmd;
 		vmd->irqs[i].virq = pci_irq_vector(dev, i);
 		err = devm_request_irq(&dev->dev, vmd->irqs[i].virq,
 				       vmd_irq, IRQF_NO_THREAD,
-- 
2.53.0




More information about the kernel-team mailing list