[PATCH] iommu/amd: Reset IRTE notification bits on every GA update

From: Jinmeng Zhou

Date: Tue Oct 20 2026 - 10:21:00 EST


From: Jinmeng Zhou <zhoujinmeng@xxxxxxxxxxxxx>

__amd_iommu_update_ga() clears GALogIntr when the vCPU starts running,
but leaves GAPPIDis untouched, so a GAPPIDis value programmed while the
vCPU was scheduled out is carried over into the running IRTE. GAPPIDis
is recomputed every time the vCPU is put, so the stale value is only
ever visible while the vCPU is running. GALogIntr and GAPPIDis are
never both in use, as GAPPI vs. GA log is selected when the IOMMU is
initialized.

Clear both bits up front and let the not-running path set whichever one
the current wakeup policy needs, so that each update fully derives the
notification bits from the new state. The only resulting difference is
that GAPPIDis is now clear, rather than stale, in IRTEs with IsRun set.
Update the kernel-doc accordingly.

Link: https://lore.kernel.org/all/20260821055611.27138-5-sarunkod@xxxxxxx/
Signed-off-by: Jinmeng Zhou <zhoujinmeng@xxxxxxxxxxxxx>
---
This is a suggested cleanup on top of v4 4/7; please feel free to squash
it into that patch for the next revision.

drivers/iommu/amd/iommu.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index d89e3ce97e57..3d4d44be1ecb 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3962,13 +3962,19 @@ static const struct irq_domain_ops amd_ir_domain_ops = {
static void __amd_iommu_update_ga(struct irte_ga *entry, int apicid,
bool wakeup_intr, bool is_running)
{
+ /*
+ * Derive the notification bits from the new state on every update so
+ * that an in-place update doesn't carry over bits from the old state.
+ */
+ entry->lo.fields_vapic.ga_log_intr = false;
+ entry->lo.fields_vapic.gappi_dis = false;
+
if (is_running) {
entry->lo.fields_vapic.destination =
APICID_TO_IRTE_DEST_LO(apicid);
entry->hi.fields.destination =
APICID_TO_IRTE_DEST_HI(apicid);
entry->lo.fields_vapic.is_run = true;
- entry->lo.fields_vapic.ga_log_intr = false;
} else {
if (amd_iommu_gappi) {
entry->lo.fields_vapic.gappi_dis = !wakeup_intr &&
@@ -3990,13 +3996,13 @@ static void __amd_iommu_update_ga(struct irte_ga *entry, int apicid,
* a vCPU, without issuing an IOMMU invalidation for the IRTE.
*
* If the vCPU is scheduled to run on pCPU (@is_running = 1), configure the
- * Destination with the pCPU's APIC ID, set IsRun, and clear GALogIntr. If the
- * vCPU is scheduled out (@is_running = 0), clear IsRun and set/clear GALogIntr
- * and GAPPIDis based on input from the caller (e.g. KVM only requests
- * wakeup_intr when the vCPU is blocking and requires a notification wake
- * event). This API is intended to be used when a vCPU is scheduled in/out (or
- * stops running for any reason), to do a fast update of IsRun, GALogIntr,
- * GAPPIDis and (conditionally) Destination.
+ * Destination with the pCPU's APIC ID, set IsRun, and clear GALogIntr and
+ * GAPPIDis. If the vCPU is scheduled out (@is_running = 0), clear IsRun and
+ * set/clear GALogIntr and GAPPIDis based on input from the caller (e.g. KVM
+ * only requests wakeup_intr when the vCPU is blocking and requires a
+ * notification wake event). This API is intended to be used when a vCPU is
+ * scheduled in/out (or stops running for any reason), to do a fast update of
+ * IsRun, GALogIntr, GAPPIDis and (conditionally) Destination.
*
* Per the IOMMU spec, the Destination, IsRun, GATag and GAPPIDis fields are
* not cached and thus don't require an invalidation to ensure the IOMMU
--
2.39.5