Skip to content

Commit f420943

Browse files
maciejsszmigierobonzini
authored andcommitted
KVM: Optimize gfn lookup in kvm_zap_gfn_range()
Introduce a memslots gfn upper bound operation and use it to optimize kvm_zap_gfn_range(). This way this handler can do a quick lookup for intersecting gfns and won't have to do a linear scan of the whole memslot set. Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com> Message-Id: <ef242146a87a335ee93b441dcf01665cb847c902.1638817641.git.maciej.szmigiero@oracle.com>
1 parent bcb63dc commit f420943

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

arch/x86/kvm/mmu/mmu.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5728,19 +5728,22 @@ static bool __kvm_zap_rmaps(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end)
57285728
{
57295729
const struct kvm_memory_slot *memslot;
57305730
struct kvm_memslots *slots;
5731+
struct kvm_memslot_iter iter;
57315732
bool flush = false;
57325733
gfn_t start, end;
5733-
int i, bkt;
5734+
int i;
57345735

57355736
if (!kvm_memslots_have_rmaps(kvm))
57365737
return flush;
57375738

57385739
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
57395740
slots = __kvm_memslots(kvm, i);
5740-
kvm_for_each_memslot(memslot, bkt, slots) {
5741+
5742+
kvm_for_each_memslot_in_gfn_range(&iter, slots, gfn_start, gfn_end) {
5743+
memslot = iter.slot;
57415744
start = max(gfn_start, memslot->base_gfn);
57425745
end = min(gfn_end, memslot->base_gfn + memslot->npages);
5743-
if (start >= end)
5746+
if (WARN_ON_ONCE(start >= end))
57445747
continue;
57455748

57465749
flush = slot_handle_level_range(kvm, memslot, kvm_zap_rmapp,
@@ -5761,6 +5764,9 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end)
57615764
bool flush;
57625765
int i;
57635766

5767+
if (WARN_ON_ONCE(gfn_end <= gfn_start))
5768+
return;
5769+
57645770
write_lock(&kvm->mmu_lock);
57655771

57665772
kvm_inc_notifier_count(kvm, gfn_start, gfn_end);

include/linux/kvm_host.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,100 @@ struct kvm_memory_slot *id_to_memslot(struct kvm_memslots *slots, int id)
846846
return NULL;
847847
}
848848

849+
/* Iterator used for walking memslots that overlap a gfn range. */
850+
struct kvm_memslot_iter {
851+
struct kvm_memslots *slots;
852+
struct rb_node *node;
853+
struct kvm_memory_slot *slot;
854+
};
855+
856+
static inline void kvm_memslot_iter_next(struct kvm_memslot_iter *iter)
857+
{
858+
iter->node = rb_next(iter->node);
859+
if (!iter->node)
860+
return;
861+
862+
iter->slot = container_of(iter->node, struct kvm_memory_slot, gfn_node[iter->slots->node_idx]);
863+
}
864+
865+
static inline void kvm_memslot_iter_start(struct kvm_memslot_iter *iter,
866+
struct kvm_memslots *slots,
867+
gfn_t start)
868+
{
869+
int idx = slots->node_idx;
870+
struct rb_node *tmp;
871+
struct kvm_memory_slot *slot;
872+
873+
iter->slots = slots;
874+
875+
/*
876+
* Find the so called "upper bound" of a key - the first node that has
877+
* its key strictly greater than the searched one (the start gfn in our case).
878+
*/
879+
iter->node = NULL;
880+
for (tmp = slots->gfn_tree.rb_node; tmp; ) {
881+
slot = container_of(tmp, struct kvm_memory_slot, gfn_node[idx]);
882+
if (start < slot->base_gfn) {
883+
iter->node = tmp;
884+
tmp = tmp->rb_left;
885+
} else {
886+
tmp = tmp->rb_right;
887+
}
888+
}
889+
890+
/*
891+
* Find the slot with the lowest gfn that can possibly intersect with
892+
* the range, so we'll ideally have slot start <= range start
893+
*/
894+
if (iter->node) {
895+
/*
896+
* A NULL previous node means that the very first slot
897+
* already has a higher start gfn.
898+
* In this case slot start > range start.
899+
*/
900+
tmp = rb_prev(iter->node);
901+
if (tmp)
902+
iter->node = tmp;
903+
} else {
904+
/* a NULL node below means no slots */
905+
iter->node = rb_last(&slots->gfn_tree);
906+
}
907+
908+
if (iter->node) {
909+
iter->slot = container_of(iter->node, struct kvm_memory_slot, gfn_node[idx]);
910+
911+
/*
912+
* It is possible in the slot start < range start case that the
913+
* found slot ends before or at range start (slot end <= range start)
914+
* and so it does not overlap the requested range.
915+
*
916+
* In such non-overlapping case the next slot (if it exists) will
917+
* already have slot start > range start, otherwise the logic above
918+
* would have found it instead of the current slot.
919+
*/
920+
if (iter->slot->base_gfn + iter->slot->npages <= start)
921+
kvm_memslot_iter_next(iter);
922+
}
923+
}
924+
925+
static inline bool kvm_memslot_iter_is_valid(struct kvm_memslot_iter *iter, gfn_t end)
926+
{
927+
if (!iter->node)
928+
return false;
929+
930+
/*
931+
* If this slot starts beyond or at the end of the range so does
932+
* every next one
933+
*/
934+
return iter->slot->base_gfn < end;
935+
}
936+
937+
/* Iterate over each memslot at least partially intersecting [start, end) range */
938+
#define kvm_for_each_memslot_in_gfn_range(iter, slots, start, end) \
939+
for (kvm_memslot_iter_start(iter, slots, start); \
940+
kvm_memslot_iter_is_valid(iter, end); \
941+
kvm_memslot_iter_next(iter))
942+
849943
/*
850944
* KVM_SET_USER_MEMORY_REGION ioctl allows the following operations:
851945
* - create a new memory slot

0 commit comments

Comments
 (0)