From: Ake Koomsin <ake@igel.co.jp>
To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Thomas Gleixner <tglx@linutronix.de>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"H . Peter Anvin" <hpa@zytor.com>, Ake Koomsin <ake@igel.co.jp>
Subject: [RFC PATCH] KVM: x86: inhibit APICv upon detecting direct APIC access from L2
Date: Mon, 7 Aug 2023 15:26:11 +0900 [thread overview]
Message-ID: <20230807062611.12596-1-ake@igel.co.jp> (raw)
Current KVM does not expect L1 hypervisor to allow L2 guest to access
APIC page directly when APICv is enabled. When this happens, KVM
emulates the access itself resulting in interrupt lost.
As this kind of hypervisor is rare, it is simpler to inhibit APICv upon
detecting direct APIC access from L2 to avoid unexpected interrupt lost.
Signed-off-by: Ake Koomsin <ake@igel.co.jp>
---
arch/x86/include/asm/kvm_host.h | 6 ++++++
arch/x86/kvm/mmu/mmu.c | 33 ++++++++++++++++++++++++++-------
arch/x86/kvm/svm/svm.h | 3 ++-
arch/x86/kvm/vmx/vmx.c | 3 ++-
4 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3bc146dfd38d..8764b11922a0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1188,6 +1188,12 @@ enum kvm_apicv_inhibit {
APICV_INHIBIT_REASON_APIC_ID_MODIFIED,
APICV_INHIBIT_REASON_APIC_BASE_MODIFIED,
+ /*
+ * APICv is disabled because L1 hypervisor allows L2 guest to access
+ * APIC directly.
+ */
+ APICV_INHIBIT_REASON_L2_PASSTHROUGH_ACCESS,
+
/******************************************************/
/* INHIBITs that are relevant only to the AMD's AVIC. */
/******************************************************/
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index ec169f5c7dce..c1150ef9fce1 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4293,6 +4293,30 @@ void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu, struct kvm_async_pf *work)
kvm_mmu_do_page_fault(vcpu, work->cr2_or_gpa, 0, true, NULL);
}
+static int __kvm_faultin_pfn_guest_mode(struct kvm_vcpu *vcpu,
+ struct kvm_page_fault *fault)
+{
+ struct kvm_memory_slot *slot = fault->slot;
+
+ /* Don't expose private memslots to L2. */
+ fault->slot = NULL;
+ fault->pfn = KVM_PFN_NOSLOT;
+ fault->map_writable = false;
+
+ /*
+ * APICv does not work when L1 hypervisor allows L2 guest to access
+ * APIC directly. As this kind of L1 hypervisor is rare, it is simpler
+ * to inhibit APICv when we detect direct APIC access from L2, and
+ * fallback to emulation path to avoid interrupt lost.
+ */
+ if (unlikely(slot && slot->id == APIC_ACCESS_PAGE_PRIVATE_MEMSLOT &&
+ kvm_apicv_activated(vcpu->kvm)))
+ kvm_set_apicv_inhibit(vcpu->kvm,
+ APICV_INHIBIT_REASON_L2_PASSTHROUGH_ACCESS);
+
+ return RET_PF_CONTINUE;
+}
+
static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
{
struct kvm_memory_slot *slot = fault->slot;
@@ -4307,13 +4331,8 @@ static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
return RET_PF_RETRY;
if (!kvm_is_visible_memslot(slot)) {
- /* Don't expose private memslots to L2. */
- if (is_guest_mode(vcpu)) {
- fault->slot = NULL;
- fault->pfn = KVM_PFN_NOSLOT;
- fault->map_writable = false;
- return RET_PF_CONTINUE;
- }
+ if (is_guest_mode(vcpu))
+ return __kvm_faultin_pfn_guest_mode(vcpu, fault);
/*
* If the APIC access page exists but is disabled, go directly
* to emulation without caching the MMIO access or creating a
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 18af7e712a5a..8d77932ee0fb 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -683,7 +683,8 @@ extern struct kvm_x86_nested_ops svm_nested_ops;
BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED) | \
BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | \
BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) | \
- BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) \
+ BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) | \
+ BIT(APICV_INHIBIT_REASON_L2_PASSTHROUGH_ACCESS) \
)
bool avic_hardware_setup(void);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index df461f387e20..f652397c9765 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8189,7 +8189,8 @@ static void vmx_hardware_unsetup(void)
BIT(APICV_INHIBIT_REASON_BLOCKIRQ) | \
BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED) | \
BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | \
- BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) \
+ BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) | \
+ BIT(APICV_INHIBIT_REASON_L2_PASSTHROUGH_ACCESS) \
)
static void vmx_vm_destroy(struct kvm *kvm)
--
2.41.0
next reply other threads:[~2023-08-07 6:30 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-07 6:26 Ake Koomsin [this message]
2023-08-07 14:00 ` [RFC PATCH] KVM: x86: inhibit APICv upon detecting direct APIC access from L2 Maxim Levitsky
2023-08-07 18:04 ` Sean Christopherson
2023-08-08 7:45 ` Ake Koomsin
2023-08-08 23:48 ` Sean Christopherson
2023-08-09 8:42 ` Ake Koomsin
2023-08-25 3:58 ` Ake Koomsin
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=20230807062611.12596-1-ake@igel.co.jp \
--to=ake@igel.co.jp \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
--cc=tglx@linutronix.de \
/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