From: Mina Almasry <almasrymina@google.com>
To: Joerg Roedel <jroedel@suse.de>,
Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>,
Vasant Hegde <vasant.hegde@amd.com>,
Jerry Snitselaar <jsnitsel@redhat.com>,
iommu@lists.linux.dev, linux-kernel@vger.kernel.org
Cc: Mina Almasry <almasrymina@google.com>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
Will Deacon <will@kernel.org>,
Robin Murphy <robin.murphy@arm.com>,
stable@vger.kernel.org
Subject: [PATCH v1] iommu/amd: Prevent spurious IRQ disable on high-rate GALog MSIs
Date: Sun, 1 Nov 2026 06:21:02 +0000 [thread overview]
Message-ID: <20261101062102.1000477-1-almasrymina@google.com> (raw)
On AMD EPYC (Turin) hosts with AVIC enabled, passed-through SR-IOV VFs
in KVM guests log interrupts targeting non-running vCPUs into the IOMMU
Guest Virtual APIC Log (GALog) ring buffer and signal the host via an
edge-triggered IntCapXT MSI (AMD-Vi*-GA).
Both __iommu_setup_intcapxt() and iommu_setup_msi() register their
handlers via request_threaded_irq(..., NULL, thread_fn, IRQF_ONESHOT,
...), using irq_default_primary_handler() (which returns
IRQ_WAKE_THREAD), while intcapxt_irqdomain_alloc() sets handle_edge_irq
which does not mask edge-triggered MSIs during oneshot thread execution.
Previously, amd_iommu_handle_irq() cleared MMIO_STATUS_OFFSET at the top
of the while (status & mask) loop before calling int_handler(iommu)
(iommu_poll_ga_log()). Under overcommitted multi-VM TCP_RR workloads
(1,000 to 10,000 flows across 4-8 VMs with vCPU overcommit), new VF
interrupts for non-running vCPUs arrive while iommu_poll_ga_log() is
draining the ring buffer, causing GALogInt to transition 0 -> 1 and fire
additional hardware edge MSIs while the IRQ thread is still inside
amd_iommu_handle_irq(). Because desc->threads_handled only increments in
irq_thread_fn() after action->thread_fn() returns, note_interrupt()
(kernel/irq/spurious.c) accounts every intermediate edge MSI as IRQ_NONE
(desc->irqs_unhandled++). Once desc->irqs_unhandled exceeds 99,900 out
of 100,000 interrupts, __report_bad_irq() logs "irq <N>: nobody cared"
and permanently disables the IOMMU GALog IRQ, breaking guest VF MSI-X
wakeups.
Fix this in two ways:
1. Drain the log ring buffer via int_handler(iommu) before writing
status & mask to MMIO_STATUS_OFFSET (clearing only the status bits
observed set so a concurrent overflow bit is never cleared without
running overflow_handler()), followed by a post-clear poll pass so
entries written before the status clear took effect are processed
without generating redundant edge MSIs or leaving entries stranded.
2. Set IRQ_IS_POLLED on the AMD-Vi MSI and IntCapXT IRQs (clearing it on
request_threaded_irq() failure paths) so note_interrupt() never
spuriously disables the dedicated IOMMU event/PPR/GALog IRQ lines
under sustained high-rate edge MSI bursts.
Discovered in production testing on AMD EPYC (Turin) hosts running
multi-VM KVM guests with passed-through SR-IOV VFs where the host
permanently disabled AMD-Vi-GA under heavy TCP_RR traffic, and verified
on the same AMD EPYC (Turin) hardware across 4-VM and 8-VM SR-IOV VF
test suites.
Fixes: 2379f3485239 ("iommu/amd: Refactor IOMMU interrupt handling logic for Event, PPR, and GA logs")
Cc: stable@vger.kernel.org
Signed-off-by: Mina Almasry <almasrymina@google.com>
---
drivers/iommu/amd/init.c | 4 ++++
drivers/iommu/amd/iommu.c | 13 ++++++++++---
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index de6db5621194..5eea9204f209 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -2404,9 +2404,11 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
if (r)
return r;
+ irq_set_status_flags(iommu->dev->irq, IRQ_IS_POLLED);
r = request_threaded_irq(iommu->dev->irq, NULL, amd_iommu_int_thread,
IRQF_ONESHOT, "AMD-Vi", iommu);
if (r) {
+ irq_clear_status_flags(iommu->dev->irq, IRQ_IS_POLLED);
pci_disable_msi(iommu->dev);
return r;
}
@@ -2579,9 +2581,11 @@ static int __iommu_setup_intcapxt(struct amd_iommu *iommu, const char *devname,
return irq;
}
+ irq_set_status_flags(irq, IRQ_IS_POLLED);
ret = request_threaded_irq(irq, NULL, thread_fn, IRQF_ONESHOT, devname,
iommu);
if (ret) {
+ irq_clear_status_flags(irq, IRQ_IS_POLLED);
irq_domain_free_irqs(irq, 1);
irq_domain_remove(domain);
return ret;
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 960e1dc05ec6..ec392da008f9 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -1112,9 +1112,6 @@ static void amd_iommu_handle_irq(void *data, const char *evt_type,
u32 mask = int_mask | overflow_mask;
while (status & mask) {
- /* Enable interrupt sources again */
- writel(mask, iommu->mmio_base + MMIO_STATUS_OFFSET);
-
if (int_handler) {
pr_devel("Processing IOMMU (ivhd%d) %s Log\n",
iommu->index, evt_type);
@@ -1124,6 +1121,16 @@ static void amd_iommu_handle_irq(void *data, const char *evt_type,
if ((status & overflow_mask) && overflow_handler)
overflow_handler(iommu);
+ /*
+ * Clear only the status bits observed above, then poll once
+ * more so entries written before the clear took effect are not
+ * left unprocessed with the interrupt status bit cleared.
+ */
+ writel(status & mask, iommu->mmio_base + MMIO_STATUS_OFFSET);
+
+ if (int_handler)
+ int_handler(iommu);
+
/*
* Hardware bug: ERBT1312
* When re-enabling interrupt (by writing 1
base-commit: 4dd16e31135ea8437a720222df34e8745f385d6d
--
2.56.0.385.gac671c819a-goog
reply other threads:[~2026-11-01 6:21 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261101062102.1000477-1-almasrymina@google.com \
--to=almasrymina@google.com \
--cc=iommu@lists.linux.dev \
--cc=joro@8bytes.org \
--cc=jroedel@suse.de \
--cc=jsnitsel@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=robin.murphy@arm.com \
--cc=stable@vger.kernel.org \
--cc=suravee.suthikulpanit@amd.com \
--cc=vasant.hegde@amd.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®