Skip to content

Commit b83ab12

Browse files
yanzhao56bonzini
authored andcommitted
KVM: x86: Add a new page-track hook to handle memslot deletion
Add a new page-track hook, track_remove_region(), that is called when a memslot DELETE operation is about to be committed. The "remove" hook will be used by KVMGT and will effectively replace the existing track_flush_slot() altogether now that KVM itself doesn't rely on the "flush" hook either. The "flush" hook is flawed as it's invoked before the memslot operation is guaranteed to succeed, i.e. KVM might ultimately keep the existing memslot without notifying external page track users, a.k.a. KVMGT. In practice, this can't currently happen on x86, but there are no guarantees that won't change in the future, not to mention that "flush" does a very poor job of describing what is happening. Pass in the gfn+nr_pages instead of the slot itself so external users, i.e. KVMGT, don't need to exposed to KVM internals (memslots). This will help set the stage for additional cleanups to the page-track APIs. Opportunistically align the existing srcu_read_lock_held() usage so that the new case doesn't stand out like a sore thumb (and not aligning the new code makes bots unhappy). Cc: Zhenyu Wang <[email protected]> Tested-by: Yongwei Ma <[email protected]> Signed-off-by: Yan Zhao <[email protected]> Co-developed-by: Sean Christopherson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 2ee05a4 commit b83ab12

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

arch/x86/include/asm/kvm_page_track.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ struct kvm_page_track_notifier_node {
4343
*/
4444
void (*track_flush_slot)(struct kvm *kvm, struct kvm_memory_slot *slot,
4545
struct kvm_page_track_notifier_node *node);
46+
47+
/*
48+
* Invoked when a memory region is removed from the guest. Or in KVM
49+
* terms, when a memslot is deleted.
50+
*
51+
* @gfn: base gfn of the region being removed
52+
* @nr_pages: number of pages in the to-be-removed region
53+
* @node: this node
54+
*/
55+
void (*track_remove_region)(gfn_t gfn, unsigned long nr_pages,
56+
struct kvm_page_track_notifier_node *node);
4657
};
4758

4859
int kvm_page_track_init(struct kvm *kvm);
@@ -75,6 +86,7 @@ kvm_page_track_unregister_notifier(struct kvm *kvm,
7586
void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
7687
int bytes);
7788
void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot);
89+
void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot);
7890

7991
bool kvm_page_track_has_external_user(struct kvm *kvm);
8092

arch/x86/kvm/mmu/page_track.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new,
270270

271271
idx = srcu_read_lock(&head->track_srcu);
272272
hlist_for_each_entry_srcu(n, &head->track_notifier_list, node,
273-
srcu_read_lock_held(&head->track_srcu))
273+
srcu_read_lock_held(&head->track_srcu))
274274
if (n->track_write)
275275
n->track_write(gpa, new, bytes, n);
276276
srcu_read_unlock(&head->track_srcu, idx);
@@ -298,12 +298,35 @@ void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
298298

299299
idx = srcu_read_lock(&head->track_srcu);
300300
hlist_for_each_entry_srcu(n, &head->track_notifier_list, node,
301-
srcu_read_lock_held(&head->track_srcu))
301+
srcu_read_lock_held(&head->track_srcu))
302302
if (n->track_flush_slot)
303303
n->track_flush_slot(kvm, slot, n);
304304
srcu_read_unlock(&head->track_srcu, idx);
305305
}
306306

307+
/*
308+
* Notify external page track nodes that a memory region is being removed from
309+
* the VM, e.g. so that users can free any associated metadata.
310+
*/
311+
void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
312+
{
313+
struct kvm_page_track_notifier_head *head;
314+
struct kvm_page_track_notifier_node *n;
315+
int idx;
316+
317+
head = &kvm->arch.track_notifier_head;
318+
319+
if (hlist_empty(&head->track_notifier_list))
320+
return;
321+
322+
idx = srcu_read_lock(&head->track_srcu);
323+
hlist_for_each_entry_srcu(n, &head->track_notifier_list, node,
324+
srcu_read_lock_held(&head->track_srcu))
325+
if (n->track_remove_region)
326+
n->track_remove_region(slot->base_gfn, slot->npages, n);
327+
srcu_read_unlock(&head->track_srcu, idx);
328+
}
329+
307330
bool kvm_page_track_has_external_user(struct kvm *kvm)
308331
{
309332
return !hlist_empty(&kvm->arch.track_notifier_head.track_notifier_list);

arch/x86/kvm/x86.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12793,6 +12793,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
1279312793
const struct kvm_memory_slot *new,
1279412794
enum kvm_mr_change change)
1279512795
{
12796+
if (change == KVM_MR_DELETE)
12797+
kvm_page_track_delete_slot(kvm, old);
12798+
1279612799
if (!kvm->arch.n_requested_mmu_pages &&
1279712800
(change == KVM_MR_CREATE || change == KVM_MR_DELETE)) {
1279812801
unsigned long nr_mmu_pages;

0 commit comments

Comments
 (0)