* FAILED: patch "[PATCH] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem" failed to apply to 7.2-stable tree
@ 2026-08-31 12:25 gregkh
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
0 siblings, 1 reply; 9+ messages in thread
From: gregkh @ 2026-08-31 12:25 UTC (permalink / raw)
To: seanjc, ackerleytng, imv4bel, joro, michael.roth, tabba,
thomas.lendacky
Cc: stable
The patch below does not apply to the 7.2-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.2.y
git checkout FETCH_HEAD
git cherry-pick -x d1a3c216233413f57f5341a9b878b7e2dde7e785
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026083158-cornmeal-overhang-51bb@gregkh' --subject-prefix 'PATCH 7.2.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d1a3c216233413f57f5341a9b878b7e2dde7e785 Mon Sep 17 00:00:00 2001
From: Sean Christopherson <seanjc@google.com>
Date: Thu, 9 Jul 2026 13:49:40 -0700
Subject: [PATCH] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem
page is zapped
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Wire up a gmem_invalidate_range() call for SNP VMs, and use it to force
vCPUs to reload/recheck their guest-provided VMSA if the backing gmem
page is being invalidated, e.g. is being PUNCH_HOLE'd. Use the same core
logic to handle invalidations as VMX does for the APIC-access page, as the
two concepts are nearly identical: shove the physical address of a page
into the vCPU's control structure:
1. Snapshot the invalidation sequence counter
2. Grab the pfn (from guest_memfd in this case)
3. Acquire mmu_lock for read
4. Re-request reload if retry is needed, otherwise commit the change.
Note, the re-request action in #4 is necessary as KVM's retry logic is
fuzzy, i.e. can get false positives. If the guest_memfd page has been
dropped, at some point a subsequent reload will fail to get a PFN from
guest_memfd, and KVM will fail KVM_RUN. If the retry was due to a false
positive, KVM will retry until there are no relevant MMU notifier events
(and will retry in the "outer" loop, i.e. will drop locks and resched as
needed).
Note #2! Take care to invalidate the VMSA when a relevant memslot is
DELETED or MOVED, as invalidations in response to PUNCH_HOLE are predicated
on memslot bindings (KVM doesn't know what GFN range(s) to invalidate
without a binding). And more importantly, the VMSA mapping requires a
memslot, i.e. must be invalidated if its memslots disappears, regardless of
the state of the underlying guest_memfd inode.
Failure to invalidate the vCPU's control.vmsa_pa (which is checked by
pre_sev_run()) can prevent KVM from properly freeing the page as firmware
will reject the RMPUPDATE to reclaim the page with FAIL_INUSE if the vCPU
is actively running, i.e. if VMSA page is in-use. That in turn leads to an
RMP #PF on the next use, as the page will still be assigned to the SNP VM.
SEV-SNP: RMPUPDATE failed for PFN 78d198, pg_level: 1, ret: 3
SEV-SNP: PFN 0x78d198, RMP entry: [0xfff0000000144001 - 0x000000000000000f]
CPU: 3 UID: 0 PID: 31345 Comm: sev_snp_vmsa_pu Tainted: G U O
Tainted: [U]=USER, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
Call Trace:
<TASK>
dump_stack_lvl+0x54/0x70
rmpupdate+0x12c/0x140
rmp_make_shared+0x3b/0x60
sev_gmem_invalidate+0xe0/0x170 [kvm_amd]
delete_from_page_cache_batch+0x1d8/0x220
truncate_inode_pages_range+0x120/0x3d0
kvm_gmem_fallocate+0x19a/0x270 [kvm]
vfs_fallocate+0x1bc/0x1f0
__x64_sys_fallocate+0x48/0x70
do_syscall_64+0x10a/0x480
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x496c7e
</TASK>
------------[ cut here ]------------
SEV: Failed to update RMP entry for PFN 0x78d198 error -14
WARNING: arch/x86/kvm/svm/sev.c:5160 at sev_gmem_invalidate+0x126/0x170 [kvm_amd], CPU#3: sev_snp_vmsa_pu/31345
CPU: 3 UID: 0 PID: 31345 Comm: sev_snp_vmsa_pu Tainted: G U O
Tainted: [U]=USER, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
RIP: 0010:sev_gmem_invalidate+0x12b/0x170 [kvm_amd]
Call Trace:
<TASK>
delete_from_page_cache_batch+0x1d8/0x220
truncate_inode_pages_range+0x120/0x3d0
kvm_gmem_fallocate+0x19a/0x270 [kvm]
vfs_fallocate+0x1bc/0x1f0
__x64_sys_fallocate+0x48/0x70
do_syscall_64+0x10a/0x480
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x496c7e
</TASK>
irq event stamp: 20689
hardirqs last enabled at (20699): [<ffffffff8e76092c>] __console_unlock+0x5c/0x60
hardirqs last disabled at (20708): [<ffffffff8e760911>] __console_unlock+0x41/0x60
softirqs last enabled at (20722): [<ffffffff8e6cd74e>] __irq_exit_rcu+0x7e/0x140
softirqs last disabled at (20717): [<ffffffff8e6cd74e>] __irq_exit_rcu+0x7e/0x140
---[ end trace 0000000000000000 ]---
BUG: unable to handle page fault for address: ffff99a64d198000
#PF: supervisor write access in kernel mode
#PF: error_code(0x80000003) - RMP violation
PGD 13eb001067 P4D 13eb001067 PUD 78d1d1063 PMD 1184e0063 PTE 800000078d198163
SEV-SNP: PFN 0x78d198, RMP entry: [0x6030000000144001 - 0x000000000000000f]
Oops: Oops: 0003 [#1] SMP
CPU: 3 UID: 0 PID: 31407 Comm: highlanderd_hea Tainted: G U W O
Tainted: [U]=USER, [W]=WARN, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
RIP: 0010:prep_new_page+0x67/0x220
Call Trace:
<TASK>
get_page_from_freelist+0x1c40/0x1c70
__alloc_frozen_pages_noprof+0xca/0x1f0
alloc_pages_mpol+0x10b/0x1b0
alloc_pages_noprof+0x81/0x90
pte_alloc_one+0x1b/0xd0
do_pte_missing+0xdf/0x1020
handle_mm_fault+0x7c7/0xb20
do_user_addr_fault+0x268/0x6b0
exc_page_fault+0x67/0xa0
asm_exc_page_fault+0x26/0x30
RIP: 0033:0x4a6b1e
</TASK>
gsmi: Log Shutdown Reason 0x03
CR2: ffff99a64d198000
---[ end trace 0000000000000000 ]---
RIP: 0010:prep_new_page+0x67/0x220
Drop the pseudo-TODO comment about needing to pin the page if guest_memfd
every supports migration, as integrating with invalidations events means
KVM will Just Work if/when page migration is ever supported (assuming SNP
hardware supports migrating VMSA pages).
Note #3, invalidate() and invalidate_range() have _completely_ different
semantics; the new invalidate_range() is a true invalidation, whereas the
existing invalidate() is really a "make shared" operation. Ignore the
confusing naming and poor Kconfig bundling for the moment to minimize the
delta for LTS kernels, the mess will be cleaned up shortly.
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
Closes: https://lore.kernel.org/all/aimMWzAf5b3luM0b@v4bel
Fixes: e366f92ea99e ("KVM: SEV: Support SEV-SNP AP Creation NAE event")
Cc: stable@vger.kernel.org
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Michael Roth <michael.roth@amd.com>
Cc: Jörg Rödel <joro@8bytes.org>
Cc: Fuad Tabba <tabba@google.com>
Cc: Ackerley Tng <ackerleytng@google.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-11-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 83dc5086138b..ccf23b3f0e1c 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -134,6 +134,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region)
KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from)
KVM_X86_OP_OPTIONAL(vm_move_enc_context_from)
KVM_X86_OP_OPTIONAL(guest_memory_reclaimed)
+KVM_X86_OP_OPTIONAL(reload_vmsa)
KVM_X86_OP(get_feature_msr)
KVM_X86_OP(check_emulate_instruction)
KVM_X86_OP(apic_init_signal_blocked)
@@ -148,6 +149,9 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
KVM_X86_OP_OPTIONAL_RET0(gmem_prepare)
KVM_X86_OP_OPTIONAL_RET0(gmem_max_mapping_level)
KVM_X86_OP_OPTIONAL(gmem_invalidate)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+KVM_X86_OP_OPTIONAL(gmem_invalidate_range)
+#endif
#endif
#undef KVM_X86_OP
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index b517257a6315..1c598b40e0c3 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -122,6 +122,8 @@
KVM_ARCH_REQ_FLAGS(31, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_HV_TLB_FLUSH \
KVM_ARCH_REQ_FLAGS(32, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
+#define KVM_REQ_VMSA_PAGE_RELOAD \
+ KVM_ARCH_REQ_FLAGS(33, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_UPDATE_PROTECTED_GUEST_STATE \
KVM_ARCH_REQ_FLAGS(34, KVM_REQUEST_WAIT)
@@ -1878,6 +1880,7 @@ struct kvm_x86_ops {
int (*vm_copy_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
void (*guest_memory_reclaimed)(struct kvm *kvm);
+ void (*reload_vmsa)(struct kvm_vcpu *vcpu);
int (*get_feature_msr)(u32 msr, u64 *data);
@@ -1902,6 +1905,9 @@ struct kvm_x86_ops {
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void (*gmem_invalidate)(kvm_pfn_t start, kvm_pfn_t end);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ void (*gmem_invalidate_range)(struct kvm *kvm, struct kvm_gfn_range *range);
+#endif
int (*gmem_max_mapping_level)(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
};
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 3eb1f86593b1..e2978e9a1731 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7589,6 +7589,11 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
write_lock(&kvm->mmu_lock);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ if (slot->gmem.file)
+ kvm_arch_gmem_invalidate_range(kvm, &range);
+#endif
+
if (zap_all) {
__kvm_mmu_zap_all_fast_front_half(kvm);
} else {
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 30792adcfc8e..62c6126d52c0 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3979,19 +3979,25 @@ static int snp_begin_psc(struct vcpu_svm *svm)
return snp_do_psc(svm);
}
-static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
+static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_memory_slot *slot;
+ struct kvm *kvm = vcpu->kvm;
gfn_t gfn = gpa_to_gfn(gpa);
+ unsigned long mmu_seq;
struct page *page;
kvm_pfn_t pfn;
lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex);
- /* Clear use of the VMSA. */
+ /*
+ * Clear use of the VMSA. Ensure snp_guest_vmsa_gpa is written exactly
+ * once, as it is read locklessly when responding to gfn invalidations.
+ * Pairs with the READ_ONCE() in sev_gmem_invalidate_range().
+ */
svm->vmcb->control.vmsa_pa = INVALID_PAGE;
- svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
+ WRITE_ONCE(svm->sev_es.snp_guest_vmsa_gpa, INVALID_PAGE);
/*
* When replacing the VMSA during SEV-SNP AP creation,
@@ -4006,6 +4012,9 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
if (!slot)
return;
+ mmu_seq = kvm->mmu_invalidate_seq;
+ smp_rmb();
+
/*
* The new VMSA will be private memory guest memory, so retrieve the
* PFN from the gmem backend.
@@ -4024,15 +4033,20 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
*/
svm->sev_es.snp_has_guest_vmsa = true;
- /* Use the new VMSA */
- svm->sev_es.snp_guest_vmsa_gpa = gpa;
- svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
-
+ read_lock(&kvm->mmu_lock);
/*
- * gmem pages aren't currently migratable, but if this ever changes
- * then care should be taken to ensure svm->sev_es.vmsa is pinned
- * through some other means.
+ * Save the guest-provided GPA. If retry is needed, then KVM will try
+ * again with the same GPA. If the VMSA is usable, then KVM needs to
+ * track the GPA so that the VMSA can be reloaded if the backing page
+ * for the GPA is invalidated.
*/
+ svm->sev_es.snp_guest_vmsa_gpa = gpa;
+ if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
+ kvm_make_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu);
+ else
+ svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
+ read_unlock(&kvm->mmu_lock);
+
kvm_release_page_clean(page);
}
@@ -4058,7 +4072,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
gpa = svm->sev_es.snp_pending_vmsa_gpa;
svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
- sev_snp_reload_vmsa(vcpu, gpa);
+ __sev_snp_reload_vmsa(vcpu, gpa);
/*
* Mark the vCPU as runnable for CREATE requests, indicated by a valid
@@ -4070,6 +4084,15 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
}
+void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_sev_es_state *sev_es = &to_svm(vcpu)->sev_es;
+
+ guard(mutex)(&sev_es->snp_vmsa_mutex);
+
+ __sev_snp_reload_vmsa(vcpu, sev_es->snp_guest_vmsa_gpa);
+}
+
static int sev_snp_ap_creation(struct vcpu_svm *svm)
{
struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm);
@@ -5199,6 +5222,41 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
}
}
+void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ struct kvm_vcpu *vcpu;
+ unsigned long i;
+
+ lockdep_assert_held_write(&kvm->mmu_lock);
+
+ /*
+ * An unstable result for "is SNP" is a-ok here, thanks to mmu_lock.
+ * The vCPU's VMSA GPA is invalidated before the vCPU is made visible
+ * to other tasks, and can only become valid while holding mmu_lock,
+ * after the VM is fully committed to being an SNP VM.
+ */
+ if (!____sev_snp_guest(kvm))
+ return;
+
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ /*
+ * Read snp_guest_vmsa_gpa without taking the vCPU's VMSA mutex
+ * (or its generic mutex) as mmu_lock is held, i.e. this task
+ * can't sleep. The VMSA is invalidated outside of mmu_lock,
+ * but can only become valid inside of mmu_lock, i.e. the below
+ * can get false positives, but not false negatives. A false
+ * positive is benign, as a spurious request simply forces the
+ * vCPU to re-establish its VMSA.
+ */
+ gpa_t gpa = READ_ONCE(to_svm(vcpu)->sev_es.snp_guest_vmsa_gpa);
+
+ if (VALID_PAGE(gpa) &&
+ gpa_to_gfn(gpa) >= range->start &&
+ gpa_to_gfn(gpa) < range->end)
+ kvm_make_request_and_kick(KVM_REQ_VMSA_PAGE_RELOAD, vcpu);
+ }
+}
+
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private)
{
int level, rc;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 79c818d91dda..dd51df74c2dc 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5445,12 +5445,14 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.mem_enc_register_region = sev_mem_enc_register_region,
.mem_enc_unregister_region = sev_mem_enc_unregister_region,
.guest_memory_reclaimed = sev_guest_memory_reclaimed,
+ .reload_vmsa = sev_snp_reload_vmsa,
.vm_copy_enc_context_from = sev_vm_copy_enc_context_from,
.vm_move_enc_context_from = sev_vm_move_enc_context_from,
.gmem_prepare = sev_gmem_prepare,
.gmem_invalidate = sev_gmem_invalidate,
+ .gmem_invalidate_range = sev_gmem_invalidate_range,
.gmem_max_mapping_level = sev_gmem_max_mapping_level,
#endif
.check_emulate_instruction = svm_check_emulate_instruction,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index effca6372e15..130205defffa 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -996,6 +996,7 @@ static inline struct page *snp_safe_alloc_page(void)
{
return snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);
}
+void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu);
int sev_vcpu_create(struct kvm_vcpu *vcpu);
void sev_free_vcpu(struct kvm_vcpu *vcpu);
@@ -1010,6 +1011,7 @@ extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8abd733d5173..5a5fd6211d23 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8170,6 +8170,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
goto out;
}
}
+ if (kvm_check_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu))
+ kvm_x86_call(reload_vmsa)(vcpu);
}
if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win ||
@@ -10599,6 +10601,10 @@ void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
{
kvm_x86_call(gmem_invalidate)(start, end);
}
+void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ kvm_x86_call(gmem_invalidate_range)(kvm, range);
+}
#endif
#endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ab8cfaec82d3..c00fc1740ce5 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2608,6 +2608,7 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 86690683b2fe..659b8dbe0b30 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -185,6 +185,10 @@ static void __kvm_gmem_invalidate_start(struct gmem_file *f, pgoff_t start,
}
flush |= kvm_mmu_unmap_gfn_range(kvm, &gfn_range);
+
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ kvm_arch_gmem_invalidate_range(kvm, &gfn_range);
+#endif
}
if (flush)
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests
2026-08-31 12:25 FAILED: patch "[PATCH] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem" failed to apply to 7.2-stable tree gregkh
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 2/8] KVM: SEV: Extract loading of guest-provided VMSA to a separate helper Sasha Levin
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit 42a39ad5d592aec87a70527a4e694f6210694482 ]
Track the GPA of the guest-provided VMSA used after AP_CREATION events when
running SNP guests, instead of simply tracking whether or not the vCPU is
using a guest-provided VMSA. KVM needs to know the GPA of the VMSA that's
actively being used so that it can react to MMU invalidation events, i.e.
so that KVM can drop the VMSA if its backing guest_memfd page is punched
out of existence.
Opportunistically rename snp_vmsa_gpa to clarify that it tracks the pending
VMSA GPA, whereas snp_guest_vmsa_gpa now tracks the in-use VMSA GPA.
Note! Take care to track the GPA, not the GFN, as VALID_PAGE() won't
behave correctly if an invalid GFN is converted to a GPA for checking.
Note #2! Keep snp_has_guest_vmsa so that switching to a guest-provided
VMSA is sticky, even if the guest-provided VMSA becomes invalid.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-2-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/svm/sev.c | 14 +++++++++-----
arch/x86/kvm/svm/svm.h | 3 ++-
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 0f0ea7896af5b..5924d739c7adb 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4022,6 +4022,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
/* Clear use of the VMSA */
svm->vmcb->control.vmsa_pa = INVALID_PAGE;
+ svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
/*
* When replacing the VMSA during SEV-SNP AP creation,
@@ -4029,11 +4030,11 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
*/
vmcb_mark_all_dirty(svm->vmcb);
- if (!VALID_PAGE(svm->sev_es.snp_vmsa_gpa))
+ if (!VALID_PAGE(svm->sev_es.snp_pending_vmsa_gpa))
return;
- gfn = gpa_to_gfn(svm->sev_es.snp_vmsa_gpa);
- svm->sev_es.snp_vmsa_gpa = INVALID_PAGE;
+ gfn = gpa_to_gfn(svm->sev_es.snp_pending_vmsa_gpa);
+ svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
slot = gfn_to_memslot(vcpu->kvm, gfn);
if (!slot)
@@ -4058,6 +4059,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
svm->sev_es.snp_has_guest_vmsa = true;
/* Use the new VMSA */
+ svm->sev_es.snp_guest_vmsa_gpa = gfn_to_gpa(gfn);
svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
/* Mark the vCPU as runnable */
@@ -4124,10 +4126,10 @@ static int sev_snp_ap_creation(struct vcpu_svm *svm)
return -EINVAL;
}
- target_svm->sev_es.snp_vmsa_gpa = svm->vmcb->control.exit_info_2;
+ target_svm->sev_es.snp_pending_vmsa_gpa = svm->vmcb->control.exit_info_2;
break;
case SVM_VMGEXIT_AP_DESTROY:
- target_svm->sev_es.snp_vmsa_gpa = INVALID_PAGE;
+ target_svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
break;
default:
vcpu_unimpl(vcpu, "vmgexit: invalid AP creation request [%#x] from guest\n",
@@ -4810,6 +4812,8 @@ int sev_vcpu_create(struct kvm_vcpu *vcpu)
return -ENOMEM;
svm->sev_es.vmsa = page_address(vmsa_page);
+ svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
+ svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
vcpu->arch.guest_tsc_protected = snp_is_secure_tsc_enabled(vcpu->kvm);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index d63e5878988aa..88c6b784c92b9 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -272,7 +272,8 @@ struct vcpu_sev_es_state {
u64 ghcb_registered_gpa;
struct mutex snp_vmsa_mutex; /* Used to handle concurrent updates of VMSA. */
- gpa_t snp_vmsa_gpa;
+ gpa_t snp_pending_vmsa_gpa;
+ gpa_t snp_guest_vmsa_gpa;
bool snp_ap_waiting_for_reset;
bool snp_has_guest_vmsa;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 2/8] KVM: SEV: Extract loading of guest-provided VMSA to a separate helper
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 3/8] KVM: SEV: Mark vCPU RUNNABLE after AP_CREATE, even if VMSA is unusable Sasha Levin
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit 0060569e4f18a7dee2dd8728595e909f19a23c24 ]
Extract the loading/retrieval of a guest-provided VMSA to a separate helper
so that KVM can reuse the core logic when refreshing the VMSA after an MMU
invalidation from guest_memfd.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/svm/sev.c | 52 +++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5924d739c7adb..25e5948ccc19f 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3998,29 +3998,17 @@ static int snp_begin_psc(struct vcpu_svm *svm)
return snp_do_psc(svm);
}
-/*
- * Invoked as part of svm_vcpu_reset() processing of an init event.
- */
-static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
+static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_memory_slot *slot;
+ gfn_t gfn = gpa_to_gfn(gpa);
struct page *page;
kvm_pfn_t pfn;
- gfn_t gfn;
-
- guard(mutex)(&svm->sev_es.snp_vmsa_mutex);
-
- if (!svm->sev_es.snp_ap_waiting_for_reset)
- return;
- svm->sev_es.snp_ap_waiting_for_reset = false;
-
- /* Mark the vCPU as offline and not runnable */
- vcpu->arch.pv.pv_unhalted = false;
- kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);
+ lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex);
- /* Clear use of the VMSA */
+ /* Clear use of the VMSA. */
svm->vmcb->control.vmsa_pa = INVALID_PAGE;
svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
@@ -4030,12 +4018,9 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
*/
vmcb_mark_all_dirty(svm->vmcb);
- if (!VALID_PAGE(svm->sev_es.snp_pending_vmsa_gpa))
+ if (!VALID_PAGE(gpa))
return;
- gfn = gpa_to_gfn(svm->sev_es.snp_pending_vmsa_gpa);
- svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
-
slot = gfn_to_memslot(vcpu->kvm, gfn);
if (!slot)
return;
@@ -4059,7 +4044,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
svm->sev_es.snp_has_guest_vmsa = true;
/* Use the new VMSA */
- svm->sev_es.snp_guest_vmsa_gpa = gfn_to_gpa(gfn);
+ svm->sev_es.snp_guest_vmsa_gpa = gpa;
svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
/* Mark the vCPU as runnable */
@@ -4073,6 +4058,31 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
kvm_release_page_clean(page);
}
+/*
+ * Invoked as part of svm_vcpu_reset() processing of an init event.
+ */
+static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ gpa_t gpa;
+
+ guard(mutex)(&svm->sev_es.snp_vmsa_mutex);
+
+ if (!svm->sev_es.snp_ap_waiting_for_reset)
+ return;
+
+ svm->sev_es.snp_ap_waiting_for_reset = false;
+
+ /* Mark the vCPU as offline and not runnable */
+ vcpu->arch.pv.pv_unhalted = false;
+ kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED);
+
+ gpa = svm->sev_es.snp_pending_vmsa_gpa;
+ svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
+
+ sev_snp_reload_vmsa(vcpu, gpa);
+}
+
static int sev_snp_ap_creation(struct vcpu_svm *svm)
{
struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm);
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 3/8] KVM: SEV: Mark vCPU RUNNABLE after AP_CREATE, even if VMSA is unusable
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 2/8] KVM: SEV: Extract loading of guest-provided VMSA to a separate helper Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 4/8] KVM: SEV: Wire up kvm_x86_ops.gmem_xxx() if and only if CONFIG_KVM_AMD_SEV=y Sasha Levin
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit 98ade8c48c28c227fe2e80e545ff0c57cd4712a3 ]
Always mark the vCPU as RUNNABLE after responding to AP_CREATE, even if the
guest-specified VMSA is unusable, e.g. isn't backed by a memslot or doesn't
have a backing guest_memfd page. If the VMSA is unusable, leaving the vCPU
in a non-running state will effectively hang the vCPU instead of reporting
an error to userspace. This will also allow retrying the VMSA load in the
future, to fix a bug where KVM doesn't honor guest_memfd invalidation
events, e.g. if AP_CREATION races with PUNCH_HOLE.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-4-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/svm/sev.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 25e5948ccc19f..e105335f4da79 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4047,9 +4047,6 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
svm->sev_es.snp_guest_vmsa_gpa = gpa;
svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
- /* Mark the vCPU as runnable */
- kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
-
/*
* gmem pages aren't currently migratable, but if this ever changes
* then care should be taken to ensure svm->sev_es.vmsa is pinned
@@ -4081,6 +4078,15 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
sev_snp_reload_vmsa(vcpu, gpa);
+
+ /*
+ * Mark the vCPU as runnable for CREATE requests, indicated by a valid
+ * VMSA GPA, even if installing the VMSA failed, so that KVM_RUN will
+ * fail instead of blocking indefinitely and hanging the vCPU, e.g. if
+ * the backing guest_memfd page is unavailable.
+ */
+ if (VALID_PAGE(gpa))
+ kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
}
static int sev_snp_ap_creation(struct vcpu_svm *svm)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 4/8] KVM: SEV: Wire up kvm_x86_ops.gmem_xxx() if and only if CONFIG_KVM_AMD_SEV=y
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 2/8] KVM: SEV: Extract loading of guest-provided VMSA to a separate helper Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 3/8] KVM: SEV: Mark vCPU RUNNABLE after AP_CREATE, even if VMSA is unusable Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 5/8] KVM: x86/mmu: Fold kvm_mmu_zap_memslot() into kvm_arch_flush_shadow_memslot() Sasha Levin
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Ackerley Tng, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit 01a96ff30dde5127c37497f1e098e639e7ae152f ]
Wire up the SEV-SNP guest_memfd kvm_x86_ops hooks if and only if SEV is
actually enabled, and drop the now-unnecessary stubs. Leaving the hooks
NULL allows the static call infrastructure to elide the CALL+RET, and more
importantly, referencing the hooks if and only if SEV support is enabled
will allow conditionally definining the hooks using their corresponding
HAVE_KVM_ARCH_GMEM_XXX Kconfig.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Link: https://patch.msgid.link/20260709204948.1988414-5-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/svm/svm.c | 8 ++++----
arch/x86/kvm/svm/svm.h | 10 ----------
2 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 60495447f0ca0..1158f3e28200c 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5466,6 +5466,10 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vm_copy_enc_context_from = sev_vm_copy_enc_context_from,
.vm_move_enc_context_from = sev_vm_move_enc_context_from,
+
+ .gmem_prepare = sev_gmem_prepare,
+ .gmem_invalidate = sev_gmem_invalidate,
+ .gmem_max_mapping_level = sev_gmem_max_mapping_level,
#endif
.check_emulate_instruction = svm_check_emulate_instruction,
@@ -5477,10 +5481,6 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
.vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons,
.alloc_apic_backing_page = svm_alloc_apic_backing_page,
-
- .gmem_prepare = sev_gmem_prepare,
- .gmem_invalidate = sev_gmem_invalidate,
- .gmem_max_mapping_level = sev_gmem_max_mapping_level,
};
/*
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 88c6b784c92b9..344312bd1fd46 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -1036,16 +1036,6 @@ static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; }
static inline int sev_dev_get_attr(u32 group, u64 attr, u64 *val) { return -ENXIO; }
#define max_sev_asid 0
static inline void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) {}
-static inline int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
-{
- return 0;
-}
-static inline void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) {}
-static inline int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private)
-{
- return 0;
-}
-
static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu)
{
return NULL;
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 5/8] KVM: x86/mmu: Fold kvm_mmu_zap_memslot() into kvm_arch_flush_shadow_memslot()
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
` (2 preceding siblings ...)
2026-08-31 22:20 ` [PATCH 7.2.y 4/8] KVM: SEV: Wire up kvm_x86_ops.gmem_xxx() if and only if CONFIG_KVM_AMD_SEV=y Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 6/8] KVM: x86/mmu: Split kvm_mmu_zap_all_fast() into "front" and "back" halves Sasha Levin
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit 06d38eaa78fdac1cc889f261fa420eba8e9caa1a ]
Fold kvm_mmu_zap_memslot() into its sole caller so that its GFN range
structure can be used to trigger guest_memfd invalidations regardless of
whether KVM will do a partial or full zap of the MMU.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-8-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/mmu/mmu.c | 35 +++++++++++++++--------------------
1 file changed, 15 insertions(+), 20 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index a61750f8e1e37..acab7005b0adc 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7535,8 +7535,14 @@ static void kvm_mmu_zap_memslot_pages_and_flush(struct kvm *kvm,
kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, flush);
}
-static void kvm_mmu_zap_memslot(struct kvm *kvm,
- struct kvm_memory_slot *slot)
+static inline bool kvm_memslot_flush_zap_all(struct kvm *kvm)
+{
+ return kvm->arch.vm_type == KVM_X86_DEFAULT_VM &&
+ kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL);
+}
+
+void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
+ struct kvm_memory_slot *slot)
{
struct kvm_gfn_range range = {
.slot = slot,
@@ -7547,25 +7553,14 @@ static void kvm_mmu_zap_memslot(struct kvm *kvm,
};
bool flush;
- write_lock(&kvm->mmu_lock);
- flush = kvm_unmap_gfn_range(kvm, &range);
- kvm_mmu_zap_memslot_pages_and_flush(kvm, slot, flush);
- write_unlock(&kvm->mmu_lock);
-}
-
-static inline bool kvm_memslot_flush_zap_all(struct kvm *kvm)
-{
- return kvm->arch.vm_type == KVM_X86_DEFAULT_VM &&
- kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL);
-}
-
-void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
- struct kvm_memory_slot *slot)
-{
- if (kvm_memslot_flush_zap_all(kvm))
+ if (kvm_memslot_flush_zap_all(kvm)) {
kvm_mmu_zap_all_fast(kvm);
- else
- kvm_mmu_zap_memslot(kvm, slot);
+ } else {
+ write_lock(&kvm->mmu_lock);
+ flush = kvm_unmap_gfn_range(kvm, &range);
+ kvm_mmu_zap_memslot_pages_and_flush(kvm, slot, flush);
+ write_unlock(&kvm->mmu_lock);
+ }
}
void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 6/8] KVM: x86/mmu: Split kvm_mmu_zap_all_fast() into "front" and "back" halves
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
` (3 preceding siblings ...)
2026-08-31 22:20 ` [PATCH 7.2.y 5/8] KVM: x86/mmu: Fold kvm_mmu_zap_memslot() into kvm_arch_flush_shadow_memslot() Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 7/8] KVM: x86/mmu: Use split "zap all fast" helpers when invalidating memslot Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 8/8] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped Sasha Levin
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit b27622c4eeb125814081baaefe9175191be5b94d ]
Split kvm_mmu_zap_all_fast() into a "front half" and a "back half", where
the front half is everything that runs with mmu_lock held for write, and
the back half is the code that runs outside of mmu_lock. This will allow
putting more code inside kvm_arch_flush_shadow_memslot()'s critical section
without having to take mmu_lock twice in quick succession.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-9-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/mmu/mmu.c | 37 +++++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 12 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index acab7005b0adc..b98192d29b877 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -6896,20 +6896,11 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm)
kvm_mmu_commit_zap_page(kvm, &invalid_list);
}
-/*
- * Fast invalidate all shadow pages and use lock-break technique
- * to zap obsolete pages.
- *
- * It's required when memslot is being deleted or VM is being
- * destroyed, in these cases, we should ensure that KVM MMU does
- * not use any resource of the being-deleted slot or all slots
- * after calling the function.
- */
-static void kvm_mmu_zap_all_fast(struct kvm *kvm)
+static void __kvm_mmu_zap_all_fast_front_half(struct kvm *kvm)
{
lockdep_assert_held(&kvm->slots_lock);
+ lockdep_assert_held_write(&kvm->mmu_lock);
- write_lock(&kvm->mmu_lock);
trace_kvm_mmu_zap_all_fast(kvm);
/*
@@ -6946,8 +6937,12 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm)
kvm_make_all_cpus_request(kvm, KVM_REQ_MMU_FREE_OBSOLETE_ROOTS);
kvm_zap_obsolete_pages(kvm);
+}
- write_unlock(&kvm->mmu_lock);
+static void __kvm_mmu_zap_all_fast_back_half(struct kvm *kvm)
+{
+ lockdep_assert_held(&kvm->slots_lock);
+ lockdep_assert_not_held(&kvm->mmu_lock);
/*
* Zap the invalidated TDP MMU roots, all SPTEs must be dropped before
@@ -6961,6 +6956,24 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm)
kvm_tdp_mmu_zap_invalidated_roots(kvm, true);
}
+/*
+ * Fast invalidate all shadow pages and use lock-break technique
+ * to zap obsolete pages.
+ *
+ * It's required when memslot is being deleted or VM is being
+ * destroyed, in these cases, we should ensure that KVM MMU does
+ * not use any resource of the being-deleted slot or all slots
+ * after calling the function.
+ */
+static void kvm_mmu_zap_all_fast(struct kvm *kvm)
+{
+ write_lock(&kvm->mmu_lock);
+ __kvm_mmu_zap_all_fast_front_half(kvm);
+ write_unlock(&kvm->mmu_lock);
+
+ __kvm_mmu_zap_all_fast_back_half(kvm);
+}
+
int kvm_mmu_init_vm(struct kvm *kvm)
{
int r, i;
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 7/8] KVM: x86/mmu: Use split "zap all fast" helpers when invalidating memslot
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
` (4 preceding siblings ...)
2026-08-31 22:20 ` [PATCH 7.2.y 6/8] KVM: x86/mmu: Split kvm_mmu_zap_all_fast() into "front" and "back" halves Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 8/8] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped Sasha Levin
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable; +Cc: Sean Christopherson, Michael Roth, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit db095727ff5739f4f46ee641ee6ef450032886db ]
Manually invoke the front half and back half of the "zap all fast" flow
when invalidating a memslot so that mmu_lock is acquired at function scope
in kvm_arch_flush_shadow_memslot(). This will allow putting more code
inside the critical section without having to take mmu_lock twice in quick
succession.
Opportunistically open code checking whether or not to do the fast zap, to
discourage removing the local "zap_all" in a future cleanup, i.e. to ensure
the SLOT_ZAP_ALL quirk is queried exactly once. Processing the front half
but not the back half of the fast zap (if SLOT_ZAP_ALL were disabled
concurrently) would result in KVM unnecessarily keeping invalid TDP MMU
roots until the VM is destroyed.
No functional change intended.
Cc: stable@vger.kernel.org # 6.12.x
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-10-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Stable-dep-of: d1a3c2162334 ("KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/kvm/mmu/mmu.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index b98192d29b877..9e47f84abbe36 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7548,12 +7548,6 @@ static void kvm_mmu_zap_memslot_pages_and_flush(struct kvm *kvm,
kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, flush);
}
-static inline bool kvm_memslot_flush_zap_all(struct kvm *kvm)
-{
- return kvm->arch.vm_type == KVM_X86_DEFAULT_VM &&
- kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL);
-}
-
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
struct kvm_memory_slot *slot)
{
@@ -7564,16 +7558,23 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
.may_block = true,
.attr_filter = KVM_FILTER_PRIVATE | KVM_FILTER_SHARED,
};
+ bool zap_all = kvm->arch.vm_type == KVM_X86_DEFAULT_VM &&
+ kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL);
bool flush;
- if (kvm_memslot_flush_zap_all(kvm)) {
- kvm_mmu_zap_all_fast(kvm);
+ write_lock(&kvm->mmu_lock);
+
+ if (zap_all) {
+ __kvm_mmu_zap_all_fast_front_half(kvm);
} else {
- write_lock(&kvm->mmu_lock);
flush = kvm_unmap_gfn_range(kvm, &range);
kvm_mmu_zap_memslot_pages_and_flush(kvm, slot, flush);
- write_unlock(&kvm->mmu_lock);
}
+
+ write_unlock(&kvm->mmu_lock);
+
+ if (zap_all)
+ __kvm_mmu_zap_all_fast_back_half(kvm);
}
void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7.2.y 8/8] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
` (5 preceding siblings ...)
2026-08-31 22:20 ` [PATCH 7.2.y 7/8] KVM: x86/mmu: Use split "zap all fast" helpers when invalidating memslot Sasha Levin
@ 2026-08-31 22:20 ` Sasha Levin
6 siblings, 0 replies; 9+ messages in thread
From: Sasha Levin @ 2026-08-31 22:20 UTC (permalink / raw)
To: stable
Cc: Sean Christopherson, Hyunwoo Kim, Tom Lendacky, Michael Roth,
Jörg Rödel, Fuad Tabba, Ackerley Tng, Sasha Levin
From: Sean Christopherson <seanjc@google.com>
[ Upstream commit d1a3c216233413f57f5341a9b878b7e2dde7e785 ]
Wire up a gmem_invalidate_range() call for SNP VMs, and use it to force
vCPUs to reload/recheck their guest-provided VMSA if the backing gmem
page is being invalidated, e.g. is being PUNCH_HOLE'd. Use the same core
logic to handle invalidations as VMX does for the APIC-access page, as the
two concepts are nearly identical: shove the physical address of a page
into the vCPU's control structure:
1. Snapshot the invalidation sequence counter
2. Grab the pfn (from guest_memfd in this case)
3. Acquire mmu_lock for read
4. Re-request reload if retry is needed, otherwise commit the change.
Note, the re-request action in #4 is necessary as KVM's retry logic is
fuzzy, i.e. can get false positives. If the guest_memfd page has been
dropped, at some point a subsequent reload will fail to get a PFN from
guest_memfd, and KVM will fail KVM_RUN. If the retry was due to a false
positive, KVM will retry until there are no relevant MMU notifier events
(and will retry in the "outer" loop, i.e. will drop locks and resched as
needed).
Note #2! Take care to invalidate the VMSA when a relevant memslot is
DELETED or MOVED, as invalidations in response to PUNCH_HOLE are predicated
on memslot bindings (KVM doesn't know what GFN range(s) to invalidate
without a binding). And more importantly, the VMSA mapping requires a
memslot, i.e. must be invalidated if its memslots disappears, regardless of
the state of the underlying guest_memfd inode.
Failure to invalidate the vCPU's control.vmsa_pa (which is checked by
pre_sev_run()) can prevent KVM from properly freeing the page as firmware
will reject the RMPUPDATE to reclaim the page with FAIL_INUSE if the vCPU
is actively running, i.e. if VMSA page is in-use. That in turn leads to an
RMP #PF on the next use, as the page will still be assigned to the SNP VM.
SEV-SNP: RMPUPDATE failed for PFN 78d198, pg_level: 1, ret: 3
SEV-SNP: PFN 0x78d198, RMP entry: [0xfff0000000144001 - 0x000000000000000f]
CPU: 3 UID: 0 PID: 31345 Comm: sev_snp_vmsa_pu Tainted: G U O
Tainted: [U]=USER, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
Call Trace:
<TASK>
dump_stack_lvl+0x54/0x70
rmpupdate+0x12c/0x140
rmp_make_shared+0x3b/0x60
sev_gmem_invalidate+0xe0/0x170 [kvm_amd]
delete_from_page_cache_batch+0x1d8/0x220
truncate_inode_pages_range+0x120/0x3d0
kvm_gmem_fallocate+0x19a/0x270 [kvm]
vfs_fallocate+0x1bc/0x1f0
__x64_sys_fallocate+0x48/0x70
do_syscall_64+0x10a/0x480
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x496c7e
</TASK>
------------[ cut here ]------------
SEV: Failed to update RMP entry for PFN 0x78d198 error -14
WARNING: arch/x86/kvm/svm/sev.c:5160 at sev_gmem_invalidate+0x126/0x170 [kvm_amd], CPU#3: sev_snp_vmsa_pu/31345
CPU: 3 UID: 0 PID: 31345 Comm: sev_snp_vmsa_pu Tainted: G U O
Tainted: [U]=USER, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
RIP: 0010:sev_gmem_invalidate+0x12b/0x170 [kvm_amd]
Call Trace:
<TASK>
delete_from_page_cache_batch+0x1d8/0x220
truncate_inode_pages_range+0x120/0x3d0
kvm_gmem_fallocate+0x19a/0x270 [kvm]
vfs_fallocate+0x1bc/0x1f0
__x64_sys_fallocate+0x48/0x70
do_syscall_64+0x10a/0x480
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x496c7e
</TASK>
irq event stamp: 20689
hardirqs last enabled at (20699): [<ffffffff8e76092c>] __console_unlock+0x5c/0x60
hardirqs last disabled at (20708): [<ffffffff8e760911>] __console_unlock+0x41/0x60
softirqs last enabled at (20722): [<ffffffff8e6cd74e>] __irq_exit_rcu+0x7e/0x140
softirqs last disabled at (20717): [<ffffffff8e6cd74e>] __irq_exit_rcu+0x7e/0x140
---[ end trace 0000000000000000 ]---
BUG: unable to handle page fault for address: ffff99a64d198000
#PF: supervisor write access in kernel mode
#PF: error_code(0x80000003) - RMP violation
PGD 13eb001067 P4D 13eb001067 PUD 78d1d1063 PMD 1184e0063 PTE 800000078d198163
SEV-SNP: PFN 0x78d198, RMP entry: [0x6030000000144001 - 0x000000000000000f]
Oops: Oops: 0003 [#1] SMP
CPU: 3 UID: 0 PID: 31407 Comm: highlanderd_hea Tainted: G U W O
Tainted: [U]=USER, [W]=WARN, [O]=OOT_MODULE
Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 34.86.0-102 01/25/2026
RIP: 0010:prep_new_page+0x67/0x220
Call Trace:
<TASK>
get_page_from_freelist+0x1c40/0x1c70
__alloc_frozen_pages_noprof+0xca/0x1f0
alloc_pages_mpol+0x10b/0x1b0
alloc_pages_noprof+0x81/0x90
pte_alloc_one+0x1b/0xd0
do_pte_missing+0xdf/0x1020
handle_mm_fault+0x7c7/0xb20
do_user_addr_fault+0x268/0x6b0
exc_page_fault+0x67/0xa0
asm_exc_page_fault+0x26/0x30
RIP: 0033:0x4a6b1e
</TASK>
gsmi: Log Shutdown Reason 0x03
CR2: ffff99a64d198000
---[ end trace 0000000000000000 ]---
RIP: 0010:prep_new_page+0x67/0x220
Drop the pseudo-TODO comment about needing to pin the page if guest_memfd
every supports migration, as integrating with invalidations events means
KVM will Just Work if/when page migration is ever supported (assuming SNP
hardware supports migrating VMSA pages).
Note #3, invalidate() and invalidate_range() have _completely_ different
semantics; the new invalidate_range() is a true invalidation, whereas the
existing invalidate() is really a "make shared" operation. Ignore the
confusing naming and poor Kconfig bundling for the moment to minimize the
delta for LTS kernels, the mess will be cleaned up shortly.
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
Closes: https://lore.kernel.org/all/aimMWzAf5b3luM0b@v4bel
Fixes: e366f92ea99e ("KVM: SEV: Support SEV-SNP AP Creation NAE event")
Cc: stable@vger.kernel.org
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Michael Roth <michael.roth@amd.com>
Cc: Jörg Rödel <joro@8bytes.org>
Cc: Fuad Tabba <tabba@google.com>
Cc: Ackerley Tng <ackerleytng@google.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
Link: https://patch.msgid.link/20260709204948.1988414-11-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/include/asm/kvm-x86-ops.h | 4 ++
arch/x86/include/asm/kvm_host.h | 6 +++
arch/x86/kvm/mmu/mmu.c | 5 ++
arch/x86/kvm/svm/sev.c | 80 ++++++++++++++++++++++++++----
arch/x86/kvm/svm/svm.c | 2 +
arch/x86/kvm/svm/svm.h | 2 +
arch/x86/kvm/x86.c | 6 +++
include/linux/kvm_host.h | 1 +
virt/kvm/guest_memfd.c | 4 ++
9 files changed, 99 insertions(+), 11 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 83dc5086138b3..ccf23b3f0e1c2 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -134,6 +134,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region)
KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from)
KVM_X86_OP_OPTIONAL(vm_move_enc_context_from)
KVM_X86_OP_OPTIONAL(guest_memory_reclaimed)
+KVM_X86_OP_OPTIONAL(reload_vmsa)
KVM_X86_OP(get_feature_msr)
KVM_X86_OP(check_emulate_instruction)
KVM_X86_OP(apic_init_signal_blocked)
@@ -148,6 +149,9 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
KVM_X86_OP_OPTIONAL_RET0(gmem_prepare)
KVM_X86_OP_OPTIONAL_RET0(gmem_max_mapping_level)
KVM_X86_OP_OPTIONAL(gmem_invalidate)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+KVM_X86_OP_OPTIONAL(gmem_invalidate_range)
+#endif
#endif
#undef KVM_X86_OP
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 6db5b5f79df9b..2dfd6199a10c6 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -128,6 +128,8 @@
KVM_ARCH_REQ_FLAGS(31, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_HV_TLB_FLUSH \
KVM_ARCH_REQ_FLAGS(32, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
+#define KVM_REQ_VMSA_PAGE_RELOAD \
+ KVM_ARCH_REQ_FLAGS(33, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_UPDATE_PROTECTED_GUEST_STATE \
KVM_ARCH_REQ_FLAGS(34, KVM_REQUEST_WAIT)
@@ -1985,6 +1987,7 @@ struct kvm_x86_ops {
int (*vm_copy_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd);
void (*guest_memory_reclaimed)(struct kvm *kvm);
+ void (*reload_vmsa)(struct kvm_vcpu *vcpu);
int (*get_feature_msr)(u32 msr, u64 *data);
@@ -2009,6 +2012,9 @@ struct kvm_x86_ops {
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void (*gmem_invalidate)(kvm_pfn_t start, kvm_pfn_t end);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ void (*gmem_invalidate_range)(struct kvm *kvm, struct kvm_gfn_range *range);
+#endif
int (*gmem_max_mapping_level)(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
};
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 9e47f84abbe36..8825c75a1ca3d 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7564,6 +7564,11 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
write_lock(&kvm->mmu_lock);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ if (slot->gmem.file)
+ kvm_arch_gmem_invalidate_range(kvm, &range);
+#endif
+
if (zap_all) {
__kvm_mmu_zap_all_fast_front_half(kvm);
} else {
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index e105335f4da79..b300a91d58998 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3998,19 +3998,25 @@ static int snp_begin_psc(struct vcpu_svm *svm)
return snp_do_psc(svm);
}
-static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
+static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_memory_slot *slot;
+ struct kvm *kvm = vcpu->kvm;
gfn_t gfn = gpa_to_gfn(gpa);
+ unsigned long mmu_seq;
struct page *page;
kvm_pfn_t pfn;
lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex);
- /* Clear use of the VMSA. */
+ /*
+ * Clear use of the VMSA. Ensure snp_guest_vmsa_gpa is written exactly
+ * once, as it is read locklessly when responding to gfn invalidations.
+ * Pairs with the READ_ONCE() in sev_gmem_invalidate_range().
+ */
svm->vmcb->control.vmsa_pa = INVALID_PAGE;
- svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
+ WRITE_ONCE(svm->sev_es.snp_guest_vmsa_gpa, INVALID_PAGE);
/*
* When replacing the VMSA during SEV-SNP AP creation,
@@ -4025,6 +4031,9 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
if (!slot)
return;
+ mmu_seq = kvm->mmu_invalidate_seq;
+ smp_rmb();
+
/*
* The new VMSA will be private memory guest memory, so retrieve the
* PFN from the gmem backend.
@@ -4043,15 +4052,20 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
*/
svm->sev_es.snp_has_guest_vmsa = true;
- /* Use the new VMSA */
- svm->sev_es.snp_guest_vmsa_gpa = gpa;
- svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
-
+ read_lock(&kvm->mmu_lock);
/*
- * gmem pages aren't currently migratable, but if this ever changes
- * then care should be taken to ensure svm->sev_es.vmsa is pinned
- * through some other means.
+ * Save the guest-provided GPA. If retry is needed, then KVM will try
+ * again with the same GPA. If the VMSA is usable, then KVM needs to
+ * track the GPA so that the VMSA can be reloaded if the backing page
+ * for the GPA is invalidated.
*/
+ svm->sev_es.snp_guest_vmsa_gpa = gpa;
+ if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
+ kvm_make_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu);
+ else
+ svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
+ read_unlock(&kvm->mmu_lock);
+
kvm_release_page_clean(page);
}
@@ -4077,7 +4091,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
gpa = svm->sev_es.snp_pending_vmsa_gpa;
svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
- sev_snp_reload_vmsa(vcpu, gpa);
+ __sev_snp_reload_vmsa(vcpu, gpa);
/*
* Mark the vCPU as runnable for CREATE requests, indicated by a valid
@@ -4089,6 +4103,15 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
}
+void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_sev_es_state *sev_es = &to_svm(vcpu)->sev_es;
+
+ guard(mutex)(&sev_es->snp_vmsa_mutex);
+
+ __sev_snp_reload_vmsa(vcpu, sev_es->snp_guest_vmsa_gpa);
+}
+
static int sev_snp_ap_creation(struct vcpu_svm *svm)
{
struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm);
@@ -5218,6 +5241,41 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
}
}
+void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ struct kvm_vcpu *vcpu;
+ unsigned long i;
+
+ lockdep_assert_held_write(&kvm->mmu_lock);
+
+ /*
+ * An unstable result for "is SNP" is a-ok here, thanks to mmu_lock.
+ * The vCPU's VMSA GPA is invalidated before the vCPU is made visible
+ * to other tasks, and can only become valid while holding mmu_lock,
+ * after the VM is fully committed to being an SNP VM.
+ */
+ if (!____sev_snp_guest(kvm))
+ return;
+
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ /*
+ * Read snp_guest_vmsa_gpa without taking the vCPU's VMSA mutex
+ * (or its generic mutex) as mmu_lock is held, i.e. this task
+ * can't sleep. The VMSA is invalidated outside of mmu_lock,
+ * but can only become valid inside of mmu_lock, i.e. the below
+ * can get false positives, but not false negatives. A false
+ * positive is benign, as a spurious request simply forces the
+ * vCPU to re-establish its VMSA.
+ */
+ gpa_t gpa = READ_ONCE(to_svm(vcpu)->sev_es.snp_guest_vmsa_gpa);
+
+ if (VALID_PAGE(gpa) &&
+ gpa_to_gfn(gpa) >= range->start &&
+ gpa_to_gfn(gpa) < range->end)
+ kvm_make_request_and_kick(KVM_REQ_VMSA_PAGE_RELOAD, vcpu);
+ }
+}
+
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private)
{
int level, rc;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 1158f3e28200c..9cf88eb5b7dae 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5463,12 +5463,14 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.mem_enc_register_region = sev_mem_enc_register_region,
.mem_enc_unregister_region = sev_mem_enc_unregister_region,
.guest_memory_reclaimed = sev_guest_memory_reclaimed,
+ .reload_vmsa = sev_snp_reload_vmsa,
.vm_copy_enc_context_from = sev_vm_copy_enc_context_from,
.vm_move_enc_context_from = sev_vm_move_enc_context_from,
.gmem_prepare = sev_gmem_prepare,
.gmem_invalidate = sev_gmem_invalidate,
+ .gmem_invalidate_range = sev_gmem_invalidate_range,
.gmem_max_mapping_level = sev_gmem_max_mapping_level,
#endif
.check_emulate_instruction = svm_check_emulate_instruction,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 344312bd1fd46..cd30740e99849 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -997,6 +997,7 @@ static inline struct page *snp_safe_alloc_page(void)
{
return snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);
}
+void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu);
int sev_vcpu_create(struct kvm_vcpu *vcpu);
void sev_free_vcpu(struct kvm_vcpu *vcpu);
@@ -1011,6 +1012,7 @@ extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 47cb9eba113b1..63d4e962c622a 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -11278,6 +11278,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
goto out;
}
}
+ if (kvm_check_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu))
+ kvm_x86_call(reload_vmsa)(vcpu);
}
if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win ||
@@ -14142,6 +14144,10 @@ void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
{
kvm_x86_call(gmem_invalidate)(start, end);
}
+void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ kvm_x86_call(gmem_invalidate_range)(kvm, range);
+}
#endif
#endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index ab8cfaec82d31..c00fc1740ce58 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2608,6 +2608,7 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index db57c5766ab60..171a9da1b6855 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -185,6 +185,10 @@ static void __kvm_gmem_invalidate_start(struct gmem_file *f, pgoff_t start,
}
flush |= kvm_mmu_unmap_gfn_range(kvm, &gfn_range);
+
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+ kvm_arch_gmem_invalidate_range(kvm, &gfn_range);
+#endif
}
if (flush)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-31 22:20 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 12:25 FAILED: patch "[PATCH] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem" failed to apply to 7.2-stable tree gregkh
2026-08-31 22:20 ` [PATCH 7.2.y 1/8] KVM: SEV: Track the GPA of the guest-controlled VMSA used for SNP guests Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 2/8] KVM: SEV: Extract loading of guest-provided VMSA to a separate helper Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 3/8] KVM: SEV: Mark vCPU RUNNABLE after AP_CREATE, even if VMSA is unusable Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 4/8] KVM: SEV: Wire up kvm_x86_ops.gmem_xxx() if and only if CONFIG_KVM_AMD_SEV=y Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 5/8] KVM: x86/mmu: Fold kvm_mmu_zap_memslot() into kvm_arch_flush_shadow_memslot() Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 6/8] KVM: x86/mmu: Split kvm_mmu_zap_all_fast() into "front" and "back" halves Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 7/8] KVM: x86/mmu: Use split "zap all fast" helpers when invalidating memslot Sasha Levin
2026-08-31 22:20 ` [PATCH 7.2.y 8/8] KVM: SEV: Forcefully invalidate SNP VMSA if its backing gmem page is zapped Sasha Levin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.