From: Sairaj Kodilkar <sarunkod@amd.com>
To: "Borislav Petkov (AMD)" <bp@alien8.de>,
"H. Peter Anvin" <hpa@zytor.com>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
"Paul E. McKenney" <paulmck@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>,
Breno Leitao <leitao@debian.org>,
Christian Brauner <brauner@kernel.org>,
Dapeng Mi <dapeng1.mi@linux.intel.com>,
Dave Hansen <dave.hansen@linux.intel.com>,
"Eric Biggers" <ebiggers@kernel.org>,
Ingo Molnar <mingo@redhat.com>, Jakub Kicinski <kuba@kernel.org>,
Jonathan Corbet <corbet@lwn.net>,
Kiryl Shutsemau <kas@kernel.org>,
Li RongQing <lirongqing@baidu.com>,
Marco Elver <elver@google.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
Robin Murphy <robin.murphy@arm.com>,
"Sairaj Kodilkar" <sarunkod@amd.com>,
Sean Christopherson <seanjc@google.com>,
"Shuah Khan" <skhan@linuxfoundation.org>,
Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>,
Thomas Gleixner <tglx@kernel.org>,
"Vasant Hegde" <vasant.hegde@amd.com>,
Will Deacon <will@kernel.org>, <iommu@lists.linux.dev>,
<kvm@vger.kernel.org>, <linux-coco@lists.linux.dev>,
<linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<x86@kernel.org>
Subject: [PATCH v4 6/7] KVM: SVM: Add support for AMD IOMMU Guest APIC Physical Processor Interrupt (GAPPI)
Date: Fri, 21 Aug 2026 11:26:10 +0530 [thread overview]
Message-ID: <20260821055611.27138-7-sarunkod@amd.com> (raw)
In-Reply-To: <20260821055611.27138-1-sarunkod@amd.com>
With AVIC guest-mode interrupt remapping, device interrupts are posted into
the guest vAPIC backing page by the IOMMU. When the vCPU is not running
(IRTE[IsRun] = 0), KVM must still be notified to schedule it. The legacy
path uses the GA log.
GAPPI (Guest APIC Physical Processor Interrupt) is an alternative to the
GA log mechanism provided by the AMD IOMMU. With GAPPI enabled, the IOMMU
still updates the vAPIC backing page IRR, but the host wakeup notification
is delivered as a physical APIC interrupt to IRTE[Destination], using
IRTE[GATag][7:0] as the vector (POSTED_INTR_WAKEUP_VECTOR).
SVM follows the Intel posted-interrupt wakeup model. Each pCPU maintains
a list of blocked vCPUs that may be woken by a GAPPI delivery to that CPU.
When a vCPU blocks while waiting for an interrupt, SVM enqueues it on the
wakeup list of the pCPU on which it was previously running and passes that
same pCPU's physical APIC ID to the IOMMU to program IRTE[Destination].
The rationale is that the vCPU is likely to run again on the same pCPU,
which is common when vCPUs are pinned; targeting GAPPI notifications there
reduces unnecessary VMEXITs from GAPPI deliveries on other CPUs. SVM
registers the GAPPI handler via kvm_set_posted_intr_wakeup_handler(). On
delivery, it walks the local vCPU list and wakes vCPUs with a pending IRR.
All GAPPI logic is gated on amd_iommu_gappi. Without it, KVM and the IOMMU
falls back to the legacy GA log mechanism for vCPU wakeup.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
---
arch/x86/kvm/svm/avic.c | 72 ++++++++++++++++++++++++++++++++++++-----
arch/x86/kvm/svm/svm.c | 3 ++
arch/x86/kvm/svm/svm.h | 5 +++
3 files changed, 72 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index dd497530d365..18ac24ef40e1 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -874,6 +874,8 @@ int avic_init_vcpu(struct vcpu_svm *svm)
INIT_LIST_HEAD(&svm->ir_list);
raw_spin_lock_init(&svm->ir_list_lock);
+ svm->gappi_cpu = -1;
+
if (!enable_apicv || !irqchip_in_kernel(vcpu->kvm))
return 0;
@@ -886,6 +888,20 @@ int avic_init_vcpu(struct vcpu_svm *svm)
return ret;
}
+void avic_destroy_vcpu(struct vcpu_svm *svm)
+{
+ if (amd_iommu_gappi && svm->gappi_cpu != -1) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ kvm_pi_disable_wakeup_handler(&svm->vcpu, svm->gappi_cpu);
+ svm->gappi_cpu = -1;
+
+ local_irq_restore(flags);
+ }
+}
+
void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu)
{
avic_handle_dfr_update(vcpu);
@@ -923,8 +939,6 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
* if AVIC is enabled/uninhibited in the future.
*/
struct amd_iommu_pi_data pi_data = {
- .ga_tag = AVIC_GATAG(to_kvm_svm(kvm)->avic_vm_id,
- vcpu->vcpu_idx),
.is_guest_mode = kvm_vcpu_apicv_active(vcpu),
.vapic_addr = avic_get_backing_page_address(to_svm(vcpu)),
.vector = vector,
@@ -947,6 +961,12 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
* scheduled out, KVM will update the pCPU info when the vCPU
* is awakened and/or scheduled in. See also avic_vcpu_load().
*/
+ if (amd_iommu_gappi)
+ pi_data.ga_tag = POSTED_INTR_WAKEUP_VECTOR;
+ else
+ pi_data.ga_tag = AVIC_GATAG(to_kvm_svm(kvm)->avic_vm_id,
+ vcpu->vcpu_idx);
+
entry = svm->avic_physical_id_entry;
if (entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK) {
pi_data.apicid = entry & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK;
@@ -955,6 +975,19 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
pi_data.apicid = -1;
pi_data.wakeup_intr = entry & AVIC_PHYSICAL_ID_ENTRY_WAKEUP_INTR;
pi_data.is_running = false;
+
+ if (amd_iommu_gappi) {
+ if (svm->gappi_cpu != -1)
+ pi_data.apicid = kvm_cpu_get_apicid(svm->gappi_cpu);
+ else
+ /*
+ * If user calls KVM_IRQFD before vCPU
+ * run, use CPU 0. We don't need to add
+ * vCPU to the wakeup list as it is
+ * never been loaded.
+ */
+ pi_data.apicid = kvm_cpu_get_apicid(0);
+ }
}
ret = irq_set_vcpu_affinity(host_irq, &pi_data);
@@ -1007,7 +1040,7 @@ enum avic_vcpu_action {
};
static void avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int apicid,
- enum avic_vcpu_action action)
+ int cpu, enum avic_vcpu_action action)
{
bool wakeup_intr = (action & AVIC_START_BLOCKING);
bool is_running = apicid >= 0;
@@ -1016,6 +1049,20 @@ static void avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int apicid,
lockdep_assert_held(&svm->ir_list_lock);
+ if (amd_iommu_gappi && is_running) {
+ if (svm->gappi_cpu != -1)
+ /*
+ * Handle initial state when vCPU is loaded for the
+ * first time without any IRQ affinity.
+ */
+ kvm_pi_disable_wakeup_handler(vcpu, svm->gappi_cpu);
+
+ svm->gappi_cpu = cpu; /* Store cpu number as target for GAPPI */
+ } else if (amd_iommu_gappi) {
+ apicid = kvm_cpu_get_apicid(svm->gappi_cpu);
+ kvm_pi_enable_wakeup_handler(vcpu, svm->gappi_cpu);
+ }
+
/*
* Here, we go through the per-vcpu ir_list to update all existing
* interrupt remapping table entry targeting this vcpu.
@@ -1083,7 +1130,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu,
WRITE_ONCE(kvm_svm->avic_physical_id_table[vcpu->vcpu_id], entry);
- avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, action);
+ avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, cpu, action);
raw_spin_unlock_irqrestore(&svm->ir_list_lock, flags);
}
@@ -1126,7 +1173,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action)
*/
raw_spin_lock_irqsave(&svm->ir_list_lock, flags);
- avic_update_iommu_vcpu_affinity(vcpu, -1, action);
+ avic_update_iommu_vcpu_affinity(vcpu, -1, -1, action);
WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_WAKEUP_INTR);
@@ -1174,7 +1221,7 @@ void avic_vcpu_put(struct kvm_vcpu *vcpu)
/*
* The vCPU was preempted while blocking, ensure its IRTEs are
- * configured to generate GA Log Interrupts.
+ * configured to request host wakeup notification.
*/
if (!(WARN_ON_ONCE(!(entry & AVIC_PHYSICAL_ID_ENTRY_WAKEUP_INTR))))
return;
@@ -1299,6 +1346,13 @@ static bool __init avic_want_avic_enabled(void)
return true;
}
+bool avic_vcpu_irq_pending(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+
+ return kvm_lapic_find_highest_irr(&svm->vcpu) >= 0;
+}
+
/*
* Note:
* - The module param avic enable both xAPIC and x2APIC mode.
@@ -1342,6 +1396,8 @@ bool __init avic_hardware_setup(void)
void avic_hardware_unsetup(void)
{
- if (avic)
- amd_iommu_register_ga_log_notifier(NULL);
+ if (!avic)
+ return;
+
+ amd_iommu_register_ga_log_notifier(NULL);
}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index e02a38da5296..0883cbf24b6d 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -1356,6 +1356,8 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
WARN_ON_ONCE(!list_empty(&svm->ir_list));
+ avic_destroy_vcpu(svm);
+
svm_leave_nested(vcpu);
svm_free_nested(svm);
@@ -5292,6 +5294,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vcpu_put = svm_vcpu_put,
.vcpu_blocking = avic_vcpu_blocking,
.vcpu_unblocking = avic_vcpu_unblocking,
+ .vcpu_irq_pending = avic_vcpu_irq_pending,
.update_exception_bitmap = svm_update_exception_bitmap,
.get_feature_msr = svm_get_feature_msr,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 5137416be593..d74777f287c8 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -362,6 +362,9 @@ struct vcpu_svm {
/* Guest GIF value, used when vGIF is not enabled */
bool guest_gif;
+
+ /* GAPPI related fields */
+ int gappi_cpu;
};
struct svm_cpu_data {
@@ -909,8 +912,10 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu);
int avic_unaccelerated_access_interception(struct kvm_vcpu *vcpu);
int avic_init_vcpu(struct vcpu_svm *svm);
+void avic_destroy_vcpu(struct vcpu_svm *svm);
void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
void avic_vcpu_put(struct kvm_vcpu *vcpu);
+bool avic_vcpu_irq_pending(struct kvm_vcpu *vcpu);
void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu);
void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu);
int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
--
2.34.1
next prev parent reply other threads:[~2026-08-21 5:59 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 5:56 [PATCH v4 0/7] Add support for AMD IOMMU GAPPI Sairaj Kodilkar
2026-08-21 5:56 ` [PATCH v4 1/7] iommu/amd: KVM: SVM: Rename cpu to apicid in IOMMU interface Sairaj Kodilkar
2026-08-21 5:56 ` [PATCH v4 2/7] iommu/amd: KVM: SVM: Rename ga_log_intr to wakeup_intr " Sairaj Kodilkar
2026-08-21 5:56 ` [PATCH v4 3/7] iommu/amd: KVM: SVM: Add explicit vCPU running state to " Sairaj Kodilkar
2026-08-21 5:56 ` [PATCH v4 4/7] iommu/amd: Program guest-mode IRTEs for GAPPI wakeup when IRTE[IsRun] = 0 Sairaj Kodilkar
2026-08-21 5:56 ` [PATCH v4 5/7] KVM: VMX: Factor out wakeup list handling code to KVM Sairaj Kodilkar
2026-08-21 5:56 ` Sairaj Kodilkar [this message]
2026-08-21 5:56 ` [PATCH v4 7/7] iommu/amd: Provide kernel command line option to enable GAPPI Sairaj Kodilkar
2026-08-21 6:13 ` Randy Dunlap
2026-08-21 7:41 ` Sairaj Kodilkar
2026-08-21 7:38 ` [PATCH v4 0/7] Add support for AMD IOMMU GAPPI Sairaj Kodilkar
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=20260821055611.27138-7-sarunkod@amd.com \
--to=sarunkod@amd.com \
--cc=akpm@linux-foundation.org \
--cc=bp@alien8.de \
--cc=brauner@kernel.org \
--cc=corbet@lwn.net \
--cc=dapeng1.mi@linux.intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=ebiggers@kernel.org \
--cc=elver@google.com \
--cc=hpa@zytor.com \
--cc=iommu@lists.linux.dev \
--cc=joro@8bytes.org \
--cc=kas@kernel.org \
--cc=kuba@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=leitao@debian.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lirongqing@baidu.com \
--cc=mingo@redhat.com \
--cc=paulmck@kernel.org \
--cc=pbonzini@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=robin.murphy@arm.com \
--cc=seanjc@google.com \
--cc=skhan@linuxfoundation.org \
--cc=suravee.suthikulpanit@amd.com \
--cc=tglx@kernel.org \
--cc=vasant.hegde@amd.com \
--cc=will@kernel.org \
--cc=x86@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