* [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker
@ 2024-09-13 21:43 Vipin Sharma
2024-09-13 21:43 ` [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op Vipin Sharma
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Vipin Sharma @ 2024-09-13 21:43 UTC (permalink / raw)
To: seanjc, pbonzini
Cc: dmatlack, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux,
kvm, linux-kernel, Vipin Sharma
This series is extracted out from the NUMA aware page table series[1].
MMU shrinker changes were in patches 1 to 9 in the old series.
This series is changing KVM MMU shrinker behaviour by emptying MMU page
caches which are used during page fault and MMU load operations. It also
incorporates feedback from the NUMA aware page table series[1] regarding
MMU shrinker.
KVM MMU shrinker has not been very effective in alleviating pain under
memory pressure. It frees up the pages actively being used which results
in VM degradation. VM will take fault and bring them again in page
tables. More discussions happened at [2]. Overall, consensus was to
reprupose it into the code which frees pages from KVM MMU page caches.
Recently [3], there was a discussion to disable shrinker for TDP MMU.
Revival of this series is result of that discussion.
There are two major differences from the old series.
1. There is no global accounting of cache pages. It is dynamically
calculated in mmu_shrink_count(). This has two effects; i) counting will
be inaccurate but code is much simpler, and ii) kvm_lock being used
here, this should be fine as mmu_shrink_scan() also holds the lock
for its operation.
2. Only empty mmu_shadow_page_cache and mmu_shadowed_info_cache. This
version doesn't empty split_shadow_page_cache as it is used only
during dirty logging operation and is one per VM unlike other two
which are per vCPU. I am not fully convinced that adding it is needed
as it will add the cost of adding one more mutex and synchronizing it
in shrinker. Also, if a VM is being dirty tracked most likely it will
be migrated (memory pressure might be the reason in the first place)
so better to not hinder migration effort and let vCPUs free up their
caches. If someone convinces me to add split cache as well then I can
send a separate patch to add that as well.
[1] https://lore.kernel.org/kvm/20230306224127.1689967-1-vipinsh@google.com/
[2] https://lore.kernel.org/lkml/Y45dldZnI6OIf+a5@google.com/
[3] https://lore.kernel.org/kvm/20240819214014.GA2313467.vipinsh@google.com/#t
v1:
- No global counting of pages in cache. As this number might not remain
same between calls of mmu_shrink_count() and mmu_shrink_scan().
- Count cache pages in mmu_shrink_count(). KVM can tolerate inaccuracy
here.
- Empty mmu_shadow_page_cache and mmu_shadowed_info_cache only. Don't
empty split_shadow_page_cache.
v0: Patches 1-9 from NUMA aware page table series.
https://lore.kernel.org/kvm/20230306224127.1689967-1-vipinsh@google.com/
Vipin Sharma (2):
KVM: x86/mmu: Change KVM mmu shrinker to no-op
KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches
arch/x86/include/asm/kvm_host.h | 7 +-
arch/x86/kvm/mmu/mmu.c | 139 +++++++++++++-------------------
arch/x86/kvm/mmu/paging_tmpl.h | 14 ++--
include/linux/kvm_host.h | 1 +
virt/kvm/kvm_main.c | 8 +-
5 files changed, 78 insertions(+), 91 deletions(-)
base-commit: 12680d7b8ac4db2eba6237a21a93d2b0e78a52a6
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op 2024-09-13 21:43 [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker Vipin Sharma @ 2024-09-13 21:43 ` Vipin Sharma 2024-09-25 23:54 ` David Matlack 2024-09-13 21:43 ` [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches Vipin Sharma 2024-09-25 23:51 ` [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker David Matlack 2 siblings, 1 reply; 9+ messages in thread From: Vipin Sharma @ 2024-09-13 21:43 UTC (permalink / raw) To: seanjc, pbonzini Cc: dmatlack, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel, Vipin Sharma Remove global kvm_total_used_mmu_pages and page zapping flow from MMU shrinker. Keep shrinker infrastructure in place to reuse in future commits for freeing KVM page caches. Remove zapped_obsolete_pages list from struct kvm_arch{} and use local list in kvm_zap_obsolete_pages() since MMU shrinker is not using it anymore. mmu_shrink_scan() is very disruptive to VMs. It picks the first VM in the vm_list, zaps the oldest page which is most likely an upper level SPTEs and most like to be reused. Prior to TDP MMU, this is even more disruptive in nested VMs case, considering L1 SPTEs will be the oldest even though most of the entries are for L2 SPTEs. As discussed in https://lore.kernel.org/lkml/Y45dldZnI6OIf+a5@google.com/ shrinker logic has not be very useful in actually keeping VMs performant and reducing memory usage. Suggested-by: Sean Christopherson <seanjc@google.com> Suggested-by: David Matlack <dmatlack@google.com> Signed-off-by: Vipin Sharma <vipinsh@google.com> --- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/mmu/mmu.c | 92 +++------------------------------ 2 files changed, 8 insertions(+), 85 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b0c0bc0ed813..cbfe31bac6cf 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1309,7 +1309,6 @@ struct kvm_arch { bool pre_fault_allowed; struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES]; struct list_head active_mmu_pages; - struct list_head zapped_obsolete_pages; /* * A list of kvm_mmu_page structs that, if zapped, could possibly be * replaced by an NX huge page. A shadow page is on this list if its diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index d25c2b395116..213e46b55dda 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -179,7 +179,6 @@ struct kvm_shadow_walk_iterator { static struct kmem_cache *pte_list_desc_cache; struct kmem_cache *mmu_page_header_cache; -static struct percpu_counter kvm_total_used_mmu_pages; static void mmu_spte_set(u64 *sptep, u64 spte); @@ -1651,27 +1650,15 @@ static void kvm_mmu_check_sptes_at_free(struct kvm_mmu_page *sp) #endif } -/* - * This value is the sum of all of the kvm instances's - * kvm->arch.n_used_mmu_pages values. We need a global, - * aggregate version in order to make the slab shrinker - * faster - */ -static inline void kvm_mod_used_mmu_pages(struct kvm *kvm, long nr) -{ - kvm->arch.n_used_mmu_pages += nr; - percpu_counter_add(&kvm_total_used_mmu_pages, nr); -} - static void kvm_account_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) { - kvm_mod_used_mmu_pages(kvm, +1); + kvm->arch.n_used_mmu_pages++; kvm_account_pgtable_pages((void *)sp->spt, +1); } static void kvm_unaccount_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) { - kvm_mod_used_mmu_pages(kvm, -1); + kvm->arch.n_used_mmu_pages--; kvm_account_pgtable_pages((void *)sp->spt, -1); } @@ -6338,6 +6325,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) { struct kvm_mmu_page *sp, *node; int nr_zapped, batch = 0; + LIST_HEAD(invalid_list); bool unstable; restart: @@ -6371,7 +6359,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) } unstable = __kvm_mmu_prepare_zap_page(kvm, sp, - &kvm->arch.zapped_obsolete_pages, &nr_zapped); + &invalid_list, &nr_zapped); batch += nr_zapped; if (unstable) @@ -6387,7 +6375,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) * kvm_mmu_load()), and the reload in the caller ensure no vCPUs are * running with an obsolete MMU. */ - kvm_mmu_commit_zap_page(kvm, &kvm->arch.zapped_obsolete_pages); + kvm_mmu_commit_zap_page(kvm, &invalid_list); } /* @@ -6450,16 +6438,10 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm) kvm_tdp_mmu_zap_invalidated_roots(kvm); } -static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm) -{ - return unlikely(!list_empty_careful(&kvm->arch.zapped_obsolete_pages)); -} - void kvm_mmu_init_vm(struct kvm *kvm) { kvm->arch.shadow_mmio_value = shadow_mmio_value; INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); - INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages); INIT_LIST_HEAD(&kvm->arch.possible_nx_huge_pages); spin_lock_init(&kvm->arch.mmu_unsync_pages_lock); @@ -7015,65 +6997,13 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) static unsigned long mmu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - struct kvm *kvm; - int nr_to_scan = sc->nr_to_scan; - unsigned long freed = 0; - - mutex_lock(&kvm_lock); - - list_for_each_entry(kvm, &vm_list, vm_list) { - int idx; - - /* - * Never scan more than sc->nr_to_scan VM instances. - * Will not hit this condition practically since we do not try - * to shrink more than one VM and it is very unlikely to see - * !n_used_mmu_pages so many times. - */ - if (!nr_to_scan--) - break; - /* - * n_used_mmu_pages is accessed without holding kvm->mmu_lock - * here. We may skip a VM instance errorneosly, but we do not - * want to shrink a VM that only started to populate its MMU - * anyway. - */ - if (!kvm->arch.n_used_mmu_pages && - !kvm_has_zapped_obsolete_pages(kvm)) - continue; - - idx = srcu_read_lock(&kvm->srcu); - write_lock(&kvm->mmu_lock); - - if (kvm_has_zapped_obsolete_pages(kvm)) { - kvm_mmu_commit_zap_page(kvm, - &kvm->arch.zapped_obsolete_pages); - goto unlock; - } - - freed = kvm_mmu_zap_oldest_mmu_pages(kvm, sc->nr_to_scan); - -unlock: - write_unlock(&kvm->mmu_lock); - srcu_read_unlock(&kvm->srcu, idx); - - /* - * unfair on small ones - * per-vm shrinkers cry out - * sadness comes quickly - */ - list_move_tail(&kvm->vm_list, &vm_list); - break; - } - - mutex_unlock(&kvm_lock); - return freed; + return SHRINK_STOP; } static unsigned long mmu_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { - return percpu_counter_read_positive(&kvm_total_used_mmu_pages); + return SHRINK_EMPTY; } static struct shrinker *mmu_shrinker; @@ -7204,12 +7134,9 @@ int kvm_mmu_vendor_module_init(void) if (!mmu_page_header_cache) goto out; - if (percpu_counter_init(&kvm_total_used_mmu_pages, 0, GFP_KERNEL)) - goto out; - mmu_shrinker = shrinker_alloc(0, "x86-mmu"); if (!mmu_shrinker) - goto out_shrinker; + goto out; mmu_shrinker->count_objects = mmu_shrink_count; mmu_shrinker->scan_objects = mmu_shrink_scan; @@ -7219,8 +7146,6 @@ int kvm_mmu_vendor_module_init(void) return 0; -out_shrinker: - percpu_counter_destroy(&kvm_total_used_mmu_pages); out: mmu_destroy_caches(); return ret; @@ -7237,7 +7162,6 @@ void kvm_mmu_destroy(struct kvm_vcpu *vcpu) void kvm_mmu_vendor_module_exit(void) { mmu_destroy_caches(); - percpu_counter_destroy(&kvm_total_used_mmu_pages); shrinker_free(mmu_shrinker); } -- 2.46.0.662.g92d0881bb0-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op 2024-09-13 21:43 ` [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op Vipin Sharma @ 2024-09-25 23:54 ` David Matlack 0 siblings, 0 replies; 9+ messages in thread From: David Matlack @ 2024-09-25 23:54 UTC (permalink / raw) To: Vipin Sharma Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On 2024-09-13 02:43 PM, Vipin Sharma wrote: > Remove global kvm_total_used_mmu_pages and page zapping flow from MMU > shrinker. Keep shrinker infrastructure in place to reuse in future > commits for freeing KVM page caches. Remove zapped_obsolete_pages list > from struct kvm_arch{} and use local list in kvm_zap_obsolete_pages() > since MMU shrinker is not using it anymore. > > mmu_shrink_scan() is very disruptive to VMs. It picks the first VM in > the vm_list, zaps the oldest page which is most likely an upper level > SPTEs and most like to be reused. Prior to TDP MMU, this is even more > disruptive in nested VMs case, considering L1 SPTEs will be the oldest > even though most of the entries are for L2 SPTEs. > > As discussed in > https://lore.kernel.org/lkml/Y45dldZnI6OIf+a5@google.com/ shrinker logic > has not be very useful in actually keeping VMs performant and reducing > memory usage. > > Suggested-by: Sean Christopherson <seanjc@google.com> > Suggested-by: David Matlack <dmatlack@google.com> > Signed-off-by: Vipin Sharma <vipinsh@google.com> > --- Reviewed-by: David Matlack <dmatlack@google.com> ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches 2024-09-13 21:43 [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker Vipin Sharma 2024-09-13 21:43 ` [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op Vipin Sharma @ 2024-09-13 21:43 ` Vipin Sharma 2024-10-01 22:16 ` David Matlack 2024-09-25 23:51 ` [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker David Matlack 2 siblings, 1 reply; 9+ messages in thread From: Vipin Sharma @ 2024-09-13 21:43 UTC (permalink / raw) To: seanjc, pbonzini Cc: dmatlack, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel, Vipin Sharma Use MMU shrinker to iterate through all the vCPUs of all the VMs and free pages allocated in MMU memory caches. Protect cache allocation in page fault and MMU load path from MMU shrinker by using a per vCPU mutex. In MMU shrinker, move the iterated VM to the end of the VMs list so that the pain of emptying cache spread among other VMs too. The specific caches to empty are mmu_shadow_page_cache and mmu_shadowed_info_cache as these caches store whole pages. Emptying them will give more impact to shrinker compared to other caches like mmu_pte_list_desc_cache{} and mmu_page_header_cache{} Holding per vCPU mutex lock ensures that a vCPU doesn't get surprised by finding its cache emptied after filling them up for page table allocations during page fault handling and MMU load operation. Per vCPU mutex also makes sure there is only race between MMU shrinker and all other vCPUs. This should result in very less contention. Suggested-by: Sean Christopherson <seanjc@google.com> Suggested-by: David Matlack <dmatlack@google.com> Signed-off-by: Vipin Sharma <vipinsh@google.com> --- arch/x86/include/asm/kvm_host.h | 6 +++ arch/x86/kvm/mmu/mmu.c | 69 +++++++++++++++++++++++++++------ arch/x86/kvm/mmu/paging_tmpl.h | 14 ++++--- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 8 +++- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index cbfe31bac6cf..63eaf03111eb 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -811,6 +811,12 @@ struct kvm_vcpu_arch { */ struct kvm_mmu *walk_mmu; + /* + * Protect cache from getting emptied in MMU shrinker while vCPU might + * use cache for fault handling or loading MMU. As this is a per vCPU + * lock, only contention might happen when MMU shrinker runs. + */ + struct mutex mmu_memory_cache_lock; struct kvm_mmu_memory_cache mmu_pte_list_desc_cache; struct kvm_mmu_memory_cache mmu_shadow_page_cache; struct kvm_mmu_memory_cache mmu_shadowed_info_cache; diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 213e46b55dda..8e2935347615 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4524,29 +4524,33 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r != RET_PF_INVALID) return r; + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); r = mmu_topup_memory_caches(vcpu, false); if (r) - return r; + goto out_mmu_memory_cache_unlock; r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); if (r != RET_PF_CONTINUE) - return r; + goto out_mmu_memory_cache_unlock; r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; + goto out_mmu_unlock; r = make_mmu_pages_available(vcpu); if (r) - goto out_unlock; + goto out_mmu_unlock; r = direct_map(vcpu, fault); -out_unlock: +out_mmu_unlock: write_unlock(&vcpu->kvm->mmu_lock); kvm_release_pfn_clean(fault->pfn); +out_mmu_memory_cache_unlock: + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); + return r; } @@ -4617,25 +4621,28 @@ static int kvm_tdp_mmu_page_fault(struct kvm_vcpu *vcpu, if (r != RET_PF_INVALID) return r; + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); r = mmu_topup_memory_caches(vcpu, false); if (r) - return r; + goto out_mmu_memory_cache_unlock; r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); if (r != RET_PF_CONTINUE) - return r; + goto out_mmu_memory_cache_unlock; r = RET_PF_RETRY; read_lock(&vcpu->kvm->mmu_lock); if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; + goto out_mmu_unlock; r = kvm_tdp_mmu_map(vcpu, fault); -out_unlock: +out_mmu_unlock: read_unlock(&vcpu->kvm->mmu_lock); kvm_release_pfn_clean(fault->pfn); +out_mmu_memory_cache_unlock: + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); return r; } #endif @@ -5691,6 +5698,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) { int r; + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); r = mmu_topup_memory_caches(vcpu, !vcpu->arch.mmu->root_role.direct); if (r) goto out; @@ -5717,6 +5725,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) */ kvm_x86_call(flush_tlb_current)(vcpu); out: + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); return r; } @@ -6303,6 +6312,7 @@ int kvm_mmu_create(struct kvm_vcpu *vcpu) if (!vcpu->arch.mmu_shadow_page_cache.init_value) vcpu->arch.mmu_shadow_page_cache.gfp_zero = __GFP_ZERO; + mutex_init(&vcpu->arch.mmu_memory_cache_lock); vcpu->arch.mmu = &vcpu->arch.root_mmu; vcpu->arch.walk_mmu = &vcpu->arch.root_mmu; @@ -6997,13 +7007,50 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) static unsigned long mmu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - return SHRINK_STOP; + struct kvm *kvm, *next_kvm, *first_kvm = NULL; + unsigned long i, freed = 0; + struct kvm_vcpu *vcpu; + + mutex_lock(&kvm_lock); + list_for_each_entry_safe(kvm, next_kvm, &vm_list, vm_list) { + if (!first_kvm) + first_kvm = kvm; + else if (first_kvm == kvm) + break; + + list_move_tail(&kvm->vm_list, &vm_list); + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!mutex_trylock(&vcpu->arch.mmu_memory_cache_lock)) + continue; + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadow_page_cache); + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadowed_info_cache); + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); + if (freed >= sc->nr_to_scan) + goto out; + } + } +out: + mutex_unlock(&kvm_lock); + return freed; } static unsigned long mmu_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { - return SHRINK_EMPTY; + unsigned long i, count = 0; + struct kvm_vcpu *vcpu; + struct kvm *kvm; + + mutex_lock(&kvm_lock); + list_for_each_entry(kvm, &vm_list, vm_list) { + kvm_for_each_vcpu(i, vcpu, kvm) { + count += READ_ONCE(vcpu->arch.mmu_shadow_page_cache.nobjs); + count += READ_ONCE(vcpu->arch.mmu_shadowed_info_cache.nobjs); + } + } + mutex_unlock(&kvm_lock); + return !count ? SHRINK_EMPTY : count; } static struct shrinker *mmu_shrinker; diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 405bd7ceee2a..084a5c532078 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -809,13 +809,14 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault return RET_PF_EMULATE; } + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); r = mmu_topup_memory_caches(vcpu, true); if (r) - return r; + goto out_mmu_memory_cache_unlock; r = kvm_faultin_pfn(vcpu, fault, walker.pte_access); if (r != RET_PF_CONTINUE) - return r; + goto out_mmu_memory_cache_unlock; /* * Do not change pte_access if the pfn is a mmio page, otherwise @@ -840,16 +841,19 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault write_lock(&vcpu->kvm->mmu_lock); if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; + goto out_mmu_unlock; r = make_mmu_pages_available(vcpu); if (r) - goto out_unlock; + goto out_mmu_unlock; r = FNAME(fetch)(vcpu, fault, &walker); -out_unlock: +out_mmu_unlock: write_unlock(&vcpu->kvm->mmu_lock); kvm_release_pfn_clean(fault->pfn); +out_mmu_memory_cache_unlock: + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); + return r; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index b23c6d48392f..288e503f14a0 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1446,6 +1446,7 @@ void kvm_flush_remote_tlbs_memslot(struct kvm *kvm, int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min); int __kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int capacity, int min); int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc); +int kvm_mmu_empty_memory_cache(struct kvm_mmu_memory_cache *mc); void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc); void *kvm_mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc); #endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index cb2b78e92910..5d89ca218791 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -451,15 +451,21 @@ int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc) return mc->nobjs; } -void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) +int kvm_mmu_empty_memory_cache(struct kvm_mmu_memory_cache *mc) { + int freed = mc->nobjs; while (mc->nobjs) { if (mc->kmem_cache) kmem_cache_free(mc->kmem_cache, mc->objects[--mc->nobjs]); else free_page((unsigned long)mc->objects[--mc->nobjs]); } + return freed; +} +void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) +{ + kvm_mmu_empty_memory_cache(mc); kvfree(mc->objects); mc->objects = NULL; -- 2.46.0.662.g92d0881bb0-goog ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches 2024-09-13 21:43 ` [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches Vipin Sharma @ 2024-10-01 22:16 ` David Matlack 2024-10-02 16:17 ` Vipin Sharma 0 siblings, 1 reply; 9+ messages in thread From: David Matlack @ 2024-10-01 22:16 UTC (permalink / raw) To: Vipin Sharma Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On 2024-09-13 02:43 PM, Vipin Sharma wrote: > Use MMU shrinker to iterate through all the vCPUs of all the VMs and > free pages allocated in MMU memory caches. Protect cache allocation in > page fault and MMU load path from MMU shrinker by using a per vCPU > mutex. In MMU shrinker, move the iterated VM to the end of the VMs list > so that the pain of emptying cache spread among other VMs too. > > The specific caches to empty are mmu_shadow_page_cache and > mmu_shadowed_info_cache as these caches store whole pages. Emptying them > will give more impact to shrinker compared to other caches like > mmu_pte_list_desc_cache{} and mmu_page_header_cache{} > > Holding per vCPU mutex lock ensures that a vCPU doesn't get surprised > by finding its cache emptied after filling them up for page table > allocations during page fault handling and MMU load operation. Per vCPU > mutex also makes sure there is only race between MMU shrinker and all > other vCPUs. This should result in very less contention. > > Suggested-by: Sean Christopherson <seanjc@google.com> > Suggested-by: David Matlack <dmatlack@google.com> > Signed-off-by: Vipin Sharma <vipinsh@google.com> > --- > arch/x86/include/asm/kvm_host.h | 6 +++ > arch/x86/kvm/mmu/mmu.c | 69 +++++++++++++++++++++++++++------ > arch/x86/kvm/mmu/paging_tmpl.h | 14 ++++--- > include/linux/kvm_host.h | 1 + > virt/kvm/kvm_main.c | 8 +++- > 5 files changed, 81 insertions(+), 17 deletions(-) > > diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h > index cbfe31bac6cf..63eaf03111eb 100644 > --- a/arch/x86/include/asm/kvm_host.h > +++ b/arch/x86/include/asm/kvm_host.h > @@ -811,6 +811,12 @@ struct kvm_vcpu_arch { > */ > struct kvm_mmu *walk_mmu; > > + /* > + * Protect cache from getting emptied in MMU shrinker while vCPU might > + * use cache for fault handling or loading MMU. As this is a per vCPU > + * lock, only contention might happen when MMU shrinker runs. > + */ > + struct mutex mmu_memory_cache_lock; > struct kvm_mmu_memory_cache mmu_pte_list_desc_cache; > struct kvm_mmu_memory_cache mmu_shadow_page_cache; > struct kvm_mmu_memory_cache mmu_shadowed_info_cache; > diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c > index 213e46b55dda..8e2935347615 100644 > --- a/arch/x86/kvm/mmu/mmu.c > +++ b/arch/x86/kvm/mmu/mmu.c > @@ -4524,29 +4524,33 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault > if (r != RET_PF_INVALID) > return r; > > + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); > r = mmu_topup_memory_caches(vcpu, false); > if (r) > - return r; > + goto out_mmu_memory_cache_unlock; > > r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); > if (r != RET_PF_CONTINUE) > - return r; > + goto out_mmu_memory_cache_unlock; > > r = RET_PF_RETRY; > write_lock(&vcpu->kvm->mmu_lock); > > if (is_page_fault_stale(vcpu, fault)) > - goto out_unlock; > + goto out_mmu_unlock; > > r = make_mmu_pages_available(vcpu); > if (r) > - goto out_unlock; > + goto out_mmu_unlock; > > r = direct_map(vcpu, fault); > > -out_unlock: > +out_mmu_unlock: > write_unlock(&vcpu->kvm->mmu_lock); > kvm_release_pfn_clean(fault->pfn); > +out_mmu_memory_cache_unlock: > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > + > return r; > } > > @@ -4617,25 +4621,28 @@ static int kvm_tdp_mmu_page_fault(struct kvm_vcpu *vcpu, > if (r != RET_PF_INVALID) > return r; > > + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); > r = mmu_topup_memory_caches(vcpu, false); > if (r) > - return r; > + goto out_mmu_memory_cache_unlock; > > r = kvm_faultin_pfn(vcpu, fault, ACC_ALL); > if (r != RET_PF_CONTINUE) > - return r; > + goto out_mmu_memory_cache_unlock; > > r = RET_PF_RETRY; > read_lock(&vcpu->kvm->mmu_lock); > > if (is_page_fault_stale(vcpu, fault)) > - goto out_unlock; > + goto out_mmu_unlock; > > r = kvm_tdp_mmu_map(vcpu, fault); > > -out_unlock: > +out_mmu_unlock: > read_unlock(&vcpu->kvm->mmu_lock); > kvm_release_pfn_clean(fault->pfn); > +out_mmu_memory_cache_unlock: > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > return r; > } > #endif > @@ -5691,6 +5698,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) > { > int r; > > + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); > r = mmu_topup_memory_caches(vcpu, !vcpu->arch.mmu->root_role.direct); > if (r) > goto out; > @@ -5717,6 +5725,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) > */ > kvm_x86_call(flush_tlb_current)(vcpu); > out: > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > return r; > } > > @@ -6303,6 +6312,7 @@ int kvm_mmu_create(struct kvm_vcpu *vcpu) > if (!vcpu->arch.mmu_shadow_page_cache.init_value) > vcpu->arch.mmu_shadow_page_cache.gfp_zero = __GFP_ZERO; > > + mutex_init(&vcpu->arch.mmu_memory_cache_lock); > vcpu->arch.mmu = &vcpu->arch.root_mmu; > vcpu->arch.walk_mmu = &vcpu->arch.root_mmu; > > @@ -6997,13 +7007,50 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) > static unsigned long mmu_shrink_scan(struct shrinker *shrink, > struct shrink_control *sc) > { > - return SHRINK_STOP; > + struct kvm *kvm, *next_kvm, *first_kvm = NULL; > + unsigned long i, freed = 0; > + struct kvm_vcpu *vcpu; > + > + mutex_lock(&kvm_lock); > + list_for_each_entry_safe(kvm, next_kvm, &vm_list, vm_list) { > + if (!first_kvm) > + first_kvm = kvm; > + else if (first_kvm == kvm) > + break; > + > + list_move_tail(&kvm->vm_list, &vm_list); > + > + kvm_for_each_vcpu(i, vcpu, kvm) { > + if (!mutex_trylock(&vcpu->arch.mmu_memory_cache_lock)) > + continue; > + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadow_page_cache); > + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadowed_info_cache); > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > + if (freed >= sc->nr_to_scan) > + goto out; Looking at the caller in mm/shrinker.c, sc->nr_to_scan will be <= 128 (SHRINK_BATCH), which is only enough for 2 vCPUs. So I think the shrinker will only ever free 2 vCPU caches of each VM (probably the first 2 vCPUs) before reordering the list and moving onto the next VM on the next call. Does that match the behavior you observe? > + } > + } > +out: > + mutex_unlock(&kvm_lock); > + return freed; > } > > static unsigned long mmu_shrink_count(struct shrinker *shrink, > struct shrink_control *sc) > { > - return SHRINK_EMPTY; > + unsigned long i, count = 0; > + struct kvm_vcpu *vcpu; > + struct kvm *kvm; > + > + mutex_lock(&kvm_lock); > + list_for_each_entry(kvm, &vm_list, vm_list) { > + kvm_for_each_vcpu(i, vcpu, kvm) { > + count += READ_ONCE(vcpu->arch.mmu_shadow_page_cache.nobjs); > + count += READ_ONCE(vcpu->arch.mmu_shadowed_info_cache.nobjs); > + } > + } > + mutex_unlock(&kvm_lock); > + return !count ? SHRINK_EMPTY : count; > } > > static struct shrinker *mmu_shrinker; > diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h > index 405bd7ceee2a..084a5c532078 100644 > --- a/arch/x86/kvm/mmu/paging_tmpl.h > +++ b/arch/x86/kvm/mmu/paging_tmpl.h > @@ -809,13 +809,14 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault > return RET_PF_EMULATE; > } > > + mutex_lock(&vcpu->arch.mmu_memory_cache_lock); > r = mmu_topup_memory_caches(vcpu, true); > if (r) > - return r; > + goto out_mmu_memory_cache_unlock; > > r = kvm_faultin_pfn(vcpu, fault, walker.pte_access); > if (r != RET_PF_CONTINUE) > - return r; > + goto out_mmu_memory_cache_unlock; > > /* > * Do not change pte_access if the pfn is a mmio page, otherwise > @@ -840,16 +841,19 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault > write_lock(&vcpu->kvm->mmu_lock); > > if (is_page_fault_stale(vcpu, fault)) > - goto out_unlock; > + goto out_mmu_unlock; > > r = make_mmu_pages_available(vcpu); > if (r) > - goto out_unlock; > + goto out_mmu_unlock; > r = FNAME(fetch)(vcpu, fault, &walker); > > -out_unlock: > +out_mmu_unlock: > write_unlock(&vcpu->kvm->mmu_lock); > kvm_release_pfn_clean(fault->pfn); > +out_mmu_memory_cache_unlock: > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > + > return r; > } > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > index b23c6d48392f..288e503f14a0 100644 > --- a/include/linux/kvm_host.h > +++ b/include/linux/kvm_host.h > @@ -1446,6 +1446,7 @@ void kvm_flush_remote_tlbs_memslot(struct kvm *kvm, > int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min); > int __kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int capacity, int min); > int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc); > +int kvm_mmu_empty_memory_cache(struct kvm_mmu_memory_cache *mc); > void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc); > void *kvm_mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc); > #endif > diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c > index cb2b78e92910..5d89ca218791 100644 > --- a/virt/kvm/kvm_main.c > +++ b/virt/kvm/kvm_main.c > @@ -451,15 +451,21 @@ int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc) > return mc->nobjs; > } > > -void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) > +int kvm_mmu_empty_memory_cache(struct kvm_mmu_memory_cache *mc) > { > + int freed = mc->nobjs; > while (mc->nobjs) { > if (mc->kmem_cache) > kmem_cache_free(mc->kmem_cache, mc->objects[--mc->nobjs]); > else > free_page((unsigned long)mc->objects[--mc->nobjs]); > } > + return freed; > +} > > +void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) > +{ > + kvm_mmu_empty_memory_cache(mc); > kvfree(mc->objects); > > mc->objects = NULL; > -- > 2.46.0.662.g92d0881bb0-goog > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches 2024-10-01 22:16 ` David Matlack @ 2024-10-02 16:17 ` Vipin Sharma 0 siblings, 0 replies; 9+ messages in thread From: Vipin Sharma @ 2024-10-02 16:17 UTC (permalink / raw) To: David Matlack Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On Tue, Oct 1, 2024 at 3:17 PM David Matlack <dmatlack@google.com> wrote: > > On 2024-09-13 02:43 PM, Vipin Sharma wrote: > > @@ -6997,13 +7007,50 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) > > static unsigned long mmu_shrink_scan(struct shrinker *shrink, > > struct shrink_control *sc) > > { > > - return SHRINK_STOP; > > + struct kvm *kvm, *next_kvm, *first_kvm = NULL; > > + unsigned long i, freed = 0; > > + struct kvm_vcpu *vcpu; > > + > > + mutex_lock(&kvm_lock); > > + list_for_each_entry_safe(kvm, next_kvm, &vm_list, vm_list) { > > + if (!first_kvm) > > + first_kvm = kvm; > > + else if (first_kvm == kvm) > > + break; > > + > > + list_move_tail(&kvm->vm_list, &vm_list); > > + > > + kvm_for_each_vcpu(i, vcpu, kvm) { > > + if (!mutex_trylock(&vcpu->arch.mmu_memory_cache_lock)) > > + continue; > > + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadow_page_cache); > > + freed += kvm_mmu_empty_memory_cache(&vcpu->arch.mmu_shadowed_info_cache); > > + mutex_unlock(&vcpu->arch.mmu_memory_cache_lock); > > + if (freed >= sc->nr_to_scan) > > + goto out; > > Looking at the caller in mm/shrinker.c, sc->nr_to_scan will be <= 128 > (SHRINK_BATCH), which is only enough for 2 vCPUs. So I think the > shrinker will only ever free 2 vCPU caches of each VM (probably the > first 2 vCPUs) before reordering the list and moving onto the next VM on > the next call. > > Does that match the behavior you observe? > Yes, for dropping cache one time on a big VM, I get multiple calls of mmu_shrink_scan() where sc->nr_to_scan is at max 128 in each call. mmu_memory_cache_lock availability will play a role in selecting the two vCPUs. On a VM where not much faults are happening it will probably be the first two vCPUs. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker 2024-09-13 21:43 [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker Vipin Sharma 2024-09-13 21:43 ` [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op Vipin Sharma 2024-09-13 21:43 ` [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches Vipin Sharma @ 2024-09-25 23:51 ` David Matlack 2024-09-30 16:42 ` Vipin Sharma 2 siblings, 1 reply; 9+ messages in thread From: David Matlack @ 2024-09-25 23:51 UTC (permalink / raw) To: Vipin Sharma Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On 2024-09-13 02:43 PM, Vipin Sharma wrote: > This series is extracted out from the NUMA aware page table series[1]. > MMU shrinker changes were in patches 1 to 9 in the old series. I'm curious how you tested this series. Would it be posisble to write a selftest to exercise KVM's shrinker interactions? I don't think it needs to be anything fancy to be useful (e.g. just run a VM, trigger lots of shrinking, and make sure nothing blows up). There appears to be a debugfs interface which could be used to trigger shrinking from a selftest. https://docs.kernel.org/admin-guide/mm/shrinker_debugfs.html ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker 2024-09-25 23:51 ` [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker David Matlack @ 2024-09-30 16:42 ` Vipin Sharma 2024-09-30 16:50 ` David Matlack 0 siblings, 1 reply; 9+ messages in thread From: Vipin Sharma @ 2024-09-30 16:42 UTC (permalink / raw) To: David Matlack Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On Wed, Sep 25, 2024 at 4:51 PM David Matlack <dmatlack@google.com> wrote: > > On 2024-09-13 02:43 PM, Vipin Sharma wrote: > > This series is extracted out from the NUMA aware page table series[1]. > > MMU shrinker changes were in patches 1 to 9 in the old series. > > I'm curious how you tested this series. Would it be posisble to write a > selftest to exercise KVM's shrinker interactions? I don't think it needs > to be anything fancy to be useful (e.g. just run a VM, trigger lots of > shrinking, and make sure nothing blows up). My testing was dropping caches (echo 2 > /proc/sys/vm/drop_caches) in background while running dirty_log_perf_test selftest multiple times. I added printk in shrink_count() and shrink_scan() to make sure pages are being reported and released. I can write a test which can spawn a thread to drop caches and a VM which touches all of its pages to generate page faults. Only downside is it will not detect if KVM MMU shrinker is being invoked, counting and freeing pages. > > There appears to be a debugfs interface which could be used to trigger > shrinking from a selftest. > > https://docs.kernel.org/admin-guide/mm/shrinker_debugfs.html This is interesting and it does what is needed to test KVM MMU shrinker. However, this needs CONFIG_DEBUG_FS and CONFIG_SHRINKER_DEBUG. I think using shrinker_debugfs will be better, selftest can just skip if it cannot find shrinker_debugfs files. One downside is that this test will not run if these configs are not enabled. Which one do you prefer? I am preferring shrinker_debugfs but concerned about its dependency on those two configs, not sure if it is okay to have this kind of dependency in a selftests. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker 2024-09-30 16:42 ` Vipin Sharma @ 2024-09-30 16:50 ` David Matlack 0 siblings, 0 replies; 9+ messages in thread From: David Matlack @ 2024-09-30 16:50 UTC (permalink / raw) To: Vipin Sharma Cc: seanjc, pbonzini, zhi.wang.linux, weijiang.yang, mizhang, liangchen.linux, kvm, linux-kernel On Mon, Sep 30, 2024 at 9:43 AM Vipin Sharma <vipinsh@google.com> wrote: > > On Wed, Sep 25, 2024 at 4:51 PM David Matlack <dmatlack@google.com> wrote: > > > > On 2024-09-13 02:43 PM, Vipin Sharma wrote: > > > This series is extracted out from the NUMA aware page table series[1]. > > > MMU shrinker changes were in patches 1 to 9 in the old series. > > > > I'm curious how you tested this series. Would it be posisble to write a > > selftest to exercise KVM's shrinker interactions? I don't think it needs > > to be anything fancy to be useful (e.g. just run a VM, trigger lots of > > shrinking, and make sure nothing blows up). > > My testing was dropping caches (echo 2 > /proc/sys/vm/drop_caches) in > background while running dirty_log_perf_test selftest multiple times. > I added printk in shrink_count() and shrink_scan() to make sure pages > are being reported and released. > > I can write a test which can spawn a thread to drop caches and a VM > which touches all of its pages to generate page faults. Only downside > is it will not detect if KVM MMU shrinker is being invoked, counting > and freeing pages. > > > > > There appears to be a debugfs interface which could be used to trigger > > shrinking from a selftest. > > > > https://docs.kernel.org/admin-guide/mm/shrinker_debugfs.html > > This is interesting and it does what is needed to test KVM MMU > shrinker. However, this needs CONFIG_DEBUG_FS and > CONFIG_SHRINKER_DEBUG. I think using shrinker_debugfs will be better, > selftest can just skip if it cannot find shrinker_debugfs files. One > downside is that this test will not run if these configs are not > enabled. > > Which one do you prefer? I am preferring shrinker_debugfs but > concerned about its dependency on those two configs, not sure if it is > okay to have this kind of dependency in a selftests. Configs are there to be enabled. If shrinker_debugfs is the right way to test this, I don't see any reason to shy away from using it. ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-10-02 16:17 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-09-13 21:43 [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker Vipin Sharma 2024-09-13 21:43 ` [PATCH 1/2] KVM: x86/mmu: Change KVM mmu shrinker to no-op Vipin Sharma 2024-09-25 23:54 ` David Matlack 2024-09-13 21:43 ` [PATCH 2/2] KVM: x86/mmu: Use MMU shrinker to shrink KVM MMU memory caches Vipin Sharma 2024-10-01 22:16 ` David Matlack 2024-10-02 16:17 ` Vipin Sharma 2024-09-25 23:51 ` [PATCH 0/2] KVM: x86/mmu: Repurpose MMU shrinker into page cache shrinker David Matlack 2024-09-30 16:42 ` Vipin Sharma 2024-09-30 16:50 ` David Matlack
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox