Skip to content

Commit 2ebbe03

Browse files
committed
KVM: Allow arch code to elide TLB flushes when aging a young page
Add a Kconfig to allow architectures to opt-out of a TLB flush when a young page is aged, as invalidating TLB entries is not functionally required on most KVM-supported architectures. Stale TLB entries can result in false negatives and theoretically lead to suboptimal reclaim, but in practice all observations have been that the performance gained by skipping TLB flushes outweighs any performance lost by reclaiming hot pages. E.g. the primary MMUs for x86 RISC-V, s390, and PPC Book3S elide the TLB flush for ptep_clear_flush_young(), and arm64's MMU skips the trailing DSB that's required for ordering (presumably because there are optimizations related to eliding other TLB flushes when doing make-before-break). Link: https://lore.kernel.org/r/20241011021051.1557902-18-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 8564911 commit 2ebbe03

2 files changed

Lines changed: 10 additions & 14 deletions

File tree

virt/kvm/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ config KVM_GENERIC_MMU_NOTIFIER
100100
select MMU_NOTIFIER
101101
bool
102102

103+
config KVM_ELIDE_TLB_FLUSH_IF_YOUNG
104+
depends on KVM_GENERIC_MMU_NOTIFIER
105+
bool
106+
103107
config KVM_GENERIC_MEMORY_ATTRIBUTES
104108
depends on KVM_GENERIC_MMU_NOTIFIER
105109
bool

virt/kvm/kvm_main.c

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -630,15 +630,16 @@ static __always_inline kvm_mn_ret_t __kvm_handle_hva_range(struct kvm *kvm,
630630
static __always_inline int kvm_handle_hva_range(struct mmu_notifier *mn,
631631
unsigned long start,
632632
unsigned long end,
633-
gfn_handler_t handler)
633+
gfn_handler_t handler,
634+
bool flush_on_ret)
634635
{
635636
struct kvm *kvm = mmu_notifier_to_kvm(mn);
636637
const struct kvm_mmu_notifier_range range = {
637638
.start = start,
638639
.end = end,
639640
.handler = handler,
640641
.on_lock = (void *)kvm_null_fn,
641-
.flush_on_ret = true,
642+
.flush_on_ret = flush_on_ret,
642643
.may_block = false,
643644
};
644645

@@ -650,17 +651,7 @@ static __always_inline int kvm_handle_hva_range_no_flush(struct mmu_notifier *mn
650651
unsigned long end,
651652
gfn_handler_t handler)
652653
{
653-
struct kvm *kvm = mmu_notifier_to_kvm(mn);
654-
const struct kvm_mmu_notifier_range range = {
655-
.start = start,
656-
.end = end,
657-
.handler = handler,
658-
.on_lock = (void *)kvm_null_fn,
659-
.flush_on_ret = false,
660-
.may_block = false,
661-
};
662-
663-
return __kvm_handle_hva_range(kvm, &range).ret;
654+
return kvm_handle_hva_range(mn, start, end, handler, false);
664655
}
665656

666657
void kvm_mmu_invalidate_begin(struct kvm *kvm)
@@ -825,7 +816,8 @@ static int kvm_mmu_notifier_clear_flush_young(struct mmu_notifier *mn,
825816
{
826817
trace_kvm_age_hva(start, end);
827818

828-
return kvm_handle_hva_range(mn, start, end, kvm_age_gfn);
819+
return kvm_handle_hva_range(mn, start, end, kvm_age_gfn,
820+
!IS_ENABLED(CONFIG_KVM_ELIDE_TLB_FLUSH_IF_YOUNG));
829821
}
830822

831823
static int kvm_mmu_notifier_clear_young(struct mmu_notifier *mn,

0 commit comments

Comments
 (0)