* [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() [not found] <20260504224213.1049426-1-jthoughton@google.com> @ 2026-05-04 22:42 ` James Houghton 2026-05-04 23:10 ` James Houghton 2026-05-06 2:27 ` Bibo Mao 2026-05-04 22:42 ` [PATCH 2/5] KVM: loongarch: Grab MMU " James Houghton 2026-05-04 22:42 ` [PATCH 3/5] KVM: mips: " James Houghton 2 siblings, 2 replies; 12+ messages in thread From: James Houghton @ 2026-05-04 22:42 UTC (permalink / raw) To: Paolo Bonzini Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Sean Christopherson, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Bibo Mao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, James Houghton, stable kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` concurrently in the event that the KVM's `mm` is __mmput() at the same time that last reference to the KVM is being dropped. T1 T2 KVM_CREATE_VM Get VM file from T1 close VM exit_mm() close VM T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), with only the KVM srcu read lock held. T2: kvm_vm_release() ---> mmu_notifier_unregister() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), again, with only the KVM srcu read lock held. This leads to a potential double-free of kvm->arch.kvm_mmu_free_memory_cache and now with NV kvm->arch.nested_mmus. Cc: stable@vger.kernel.org Fixes: e7bf7a490c68 ("KVM: arm64: Split huge pages when dirty logging is enabled") Signed-off-by: James Houghton <jthoughton@google.com> --- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++---- arch/arm64/kvm/nested.c | 4 +++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 01e9c72d6aa7..30d5c24fcebb 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -178,6 +178,7 @@ void stage2_unmap_vm(struct kvm *kvm); int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type); void kvm_uninit_stage2_mmu(struct kvm *kvm); void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu); +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu); int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, phys_addr_t pa, unsigned long size, bool writable); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index d089c107d9b7..4bab407d43bb 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1021,7 +1021,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t void kvm_uninit_stage2_mmu(struct kvm *kvm) { - kvm_free_stage2_pgd(&kvm->arch.mmu); + lockdep_assert_held_write(&kvm->mmu_lock); + + kvm_free_stage2_pgd_locked(&kvm->arch.mmu); kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); } @@ -1095,12 +1097,14 @@ void stage2_unmap_vm(struct kvm *kvm) srcu_read_unlock(&kvm->srcu, idx); } -void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) +static void __kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu, bool locked) { struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); struct kvm_pgtable *pgt = NULL; - write_lock(&kvm->mmu_lock); + if (!locked) + write_lock(&kvm->mmu_lock); + pgt = mmu->pgt; if (pgt) { mmu->pgd_phys = 0; @@ -1111,7 +1115,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) if (kvm_is_nested_s2_mmu(kvm, mmu)) kvm_init_nested_s2_mmu(mmu); - write_unlock(&kvm->mmu_lock); + if (!locked) + write_unlock(&kvm->mmu_lock); if (pgt) { kvm_stage2_destroy(pgt); @@ -1119,6 +1124,16 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) } } +void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) +{ + __kvm_free_stage2_pgd(mmu, false); +} + +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu) +{ + __kvm_free_stage2_pgd(mmu, true); +} + static void hyp_mc_free_fn(void *addr, void *mc) { struct kvm_hyp_memcache *memcache = mc; diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 883b6c1008fb..977598bff5e6 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1190,11 +1190,13 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) { int i; + guard(write_lock)(&kvm->mmu_lock); + for (i = 0; i < kvm->arch.nested_mmus_size; i++) { struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; if (!WARN_ON(atomic_read(&mmu->refcnt))) - kvm_free_stage2_pgd(mmu); + kvm_free_stage2_pgd_locked(mmu); } kvfree(kvm->arch.nested_mmus); kvm->arch.nested_mmus = NULL; -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-04 22:42 ` [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() James Houghton @ 2026-05-04 23:10 ` James Houghton 2026-05-05 17:05 ` Sean Christopherson 2026-05-06 2:27 ` Bibo Mao 1 sibling, 1 reply; 12+ messages in thread From: James Houghton @ 2026-05-04 23:10 UTC (permalink / raw) To: jthoughton Cc: chenhuacai, gshan, jhogan, joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-mips, loongarch, maobibo, maz, oupton, pbonzini, ricarkol, seanjc, shahuang, stable, suzuki.poulose, yuzenghui, zhaotianrui On Mon, May 4, 2026 at 3:42 PM James Houghton <jthoughton@google.com> wrote: > > kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` > concurrently in the event that the KVM's `mm` is __mmput() at the > same time that last reference to the KVM is being dropped. > > T1 T2 > KVM_CREATE_VM > Get VM file from T1 > close VM > exit_mm() close VM > > T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > with only the KVM srcu read lock held. > > T2: kvm_vm_release() ---> mmu_notifier_unregister() -> > kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > again, with only the KVM srcu read lock held. > > This leads to a potential double-free of > kvm->arch.kvm_mmu_free_memory_cache and now with NV > kvm->arch.nested_mmus. > > Cc: stable@vger.kernel.org > Fixes: e7bf7a490c68 ("KVM: arm64: Split huge pages when dirty logging is enabled") > Signed-off-by: James Houghton <jthoughton@google.com> > --- > arch/arm64/include/asm/kvm_mmu.h | 1 + > arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++---- > arch/arm64/kvm/nested.c | 4 +++- > 3 files changed, 23 insertions(+), 5 deletions(-) > > diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h > index 01e9c72d6aa7..30d5c24fcebb 100644 > --- a/arch/arm64/include/asm/kvm_mmu.h > +++ b/arch/arm64/include/asm/kvm_mmu.h > @@ -178,6 +178,7 @@ void stage2_unmap_vm(struct kvm *kvm); > int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type); > void kvm_uninit_stage2_mmu(struct kvm *kvm); > void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu); > +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu); > int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, > phys_addr_t pa, unsigned long size, bool writable); > > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c > index d089c107d9b7..4bab407d43bb 100644 > --- a/arch/arm64/kvm/mmu.c > +++ b/arch/arm64/kvm/mmu.c > @@ -1021,7 +1021,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t > > void kvm_uninit_stage2_mmu(struct kvm *kvm) > { > - kvm_free_stage2_pgd(&kvm->arch.mmu); > + lockdep_assert_held_write(&kvm->mmu_lock); *facepalm*.... this doesn't account for the other callers of kvm_uninit_stage2_mmu(). They will get lockdep warnings. I've attached a diff to the bottom of this reply that *does* deal with them. :( Sorry. I'm guessing Marc or Oliver will probably want this patch to look quite different, so I'll wait to hear from them before actually sending a v2. In the meantime, I'll properly retest with lockdep enabled. > + > + kvm_free_stage2_pgd_locked(&kvm->arch.mmu); > kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); > } > > @@ -1095,12 +1097,14 @@ void stage2_unmap_vm(struct kvm *kvm) > srcu_read_unlock(&kvm->srcu, idx); > } > > -void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > +static void __kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu, bool locked) > { > struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); > struct kvm_pgtable *pgt = NULL; > > - write_lock(&kvm->mmu_lock); > + if (!locked) > + write_lock(&kvm->mmu_lock); > + > pgt = mmu->pgt; > if (pgt) { > mmu->pgd_phys = 0; > @@ -1111,7 +1115,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > if (kvm_is_nested_s2_mmu(kvm, mmu)) > kvm_init_nested_s2_mmu(mmu); > > - write_unlock(&kvm->mmu_lock); > + if (!locked) > + write_unlock(&kvm->mmu_lock); > > if (pgt) { > kvm_stage2_destroy(pgt); > @@ -1119,6 +1124,16 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > } > } > > +void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > +{ > + __kvm_free_stage2_pgd(mmu, false); > +} > + > +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu) > +{ > + __kvm_free_stage2_pgd(mmu, true); > +} > + > static void hyp_mc_free_fn(void *addr, void *mc) > { > struct kvm_hyp_memcache *memcache = mc; > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c > index 883b6c1008fb..977598bff5e6 100644 > --- a/arch/arm64/kvm/nested.c > +++ b/arch/arm64/kvm/nested.c > @@ -1190,11 +1190,13 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) > { > int i; > > + guard(write_lock)(&kvm->mmu_lock); > + > for (i = 0; i < kvm->arch.nested_mmus_size; i++) { > struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; > > if (!WARN_ON(atomic_read(&mmu->refcnt))) > - kvm_free_stage2_pgd(mmu); > + kvm_free_stage2_pgd_locked(mmu); > } > kvfree(kvm->arch.nested_mmus); > kvm->arch.nested_mmus = NULL; > -- > 2.54.0.545.g6539524ca2-goog And here is the diff that should fix this patch. (Sorry!!) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 30d5c24fcebb..e32e844943be 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -177,6 +177,7 @@ void kvm_stage2_wp_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t e void stage2_unmap_vm(struct kvm *kvm); int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type); void kvm_uninit_stage2_mmu(struct kvm *kvm); +void kvm_uninit_stage2_mmu_locked(struct kvm *kvm); void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu); void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu); int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 4bab407d43bb..98ba8116676c 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1019,14 +1019,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t return err; } -void kvm_uninit_stage2_mmu(struct kvm *kvm) -{ - lockdep_assert_held_write(&kvm->mmu_lock); - - kvm_free_stage2_pgd_locked(&kvm->arch.mmu); - kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); -} - static void stage2_unmap_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot) { @@ -1134,6 +1126,24 @@ void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu) __kvm_free_stage2_pgd(mmu, true); } +static void __kvm_uninit_stage2_mmu(struct kvm *kvm, bool locked) +{ + __kvm_free_stage2_pgd(&kvm->arch.mmu, locked); + kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); +} + +void kvm_uninit_stage2_mmu(struct kvm *kvm) +{ + __kvm_uninit_stage2_mmu(kvm, false); +} + +void kvm_uninit_stage2_mmu_locked(struct kvm *kvm) +{ + lockdep_assert_held_write(&kvm->mmu_lock); + + __kvm_uninit_stage2_mmu(kvm, true); +} + static void hyp_mc_free_fn(void *addr, void *mc) { struct kvm_hyp_memcache *memcache = mc; diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 977598bff5e6..f61f0244f0fb 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1201,7 +1201,7 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) kvfree(kvm->arch.nested_mmus); kvm->arch.nested_mmus = NULL; kvm->arch.nested_mmus_size = 0; - kvm_uninit_stage2_mmu(kvm); + kvm_uninit_stage2_mmu_locked(kvm); } /* ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-04 23:10 ` James Houghton @ 2026-05-05 17:05 ` Sean Christopherson 2026-05-05 18:01 ` James Houghton 0 siblings, 1 reply; 12+ messages in thread From: Sean Christopherson @ 2026-05-05 17:05 UTC (permalink / raw) To: James Houghton Cc: chenhuacai, gshan, jhogan, joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-mips, loongarch, maobibo, maz, oupton, pbonzini, ricarkol, shahuang, stable, suzuki.poulose, yuzenghui, zhaotianrui On Mon, May 04, 2026, James Houghton wrote: > On Mon, May 4, 2026 at 3:42 PM James Houghton <jthoughton@google.com> wrote: > > > > kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` > > concurrently in the event that the KVM's `mm` is __mmput() at the > > same time that last reference to the KVM is being dropped. > > > > T1 T2 > > KVM_CREATE_VM > > Get VM file from T1 > > close VM > > exit_mm() close VM > > > > T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > with only the KVM srcu read lock held. > > > > T2: kvm_vm_release() ---> mmu_notifier_unregister() -> > > kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > again, with only the KVM srcu read lock held. > > > > This leads to a potential double-free of > > kvm->arch.kvm_mmu_free_memory_cache and now with NV > > kvm->arch.nested_mmus. ... > > void kvm_uninit_stage2_mmu(struct kvm *kvm) > > { > > - kvm_free_stage2_pgd(&kvm->arch.mmu); > > + lockdep_assert_held_write(&kvm->mmu_lock); > > *facepalm*.... this doesn't account for the other callers of > kvm_uninit_stage2_mmu(). They will get lockdep warnings. > > I've attached a diff to the bottom of this reply that *does* deal with them. > :( Sorry. ... > > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c > > index 883b6c1008fb..977598bff5e6 100644 > > --- a/arch/arm64/kvm/nested.c > > +++ b/arch/arm64/kvm/nested.c > > @@ -1190,11 +1190,13 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) > > { > > int i; > > > > + guard(write_lock)(&kvm->mmu_lock); > > + > > for (i = 0; i < kvm->arch.nested_mmus_size; i++) { > > struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; > > > > if (!WARN_ON(atomic_read(&mmu->refcnt))) > > - kvm_free_stage2_pgd(mmu); > > + kvm_free_stage2_pgd_locked(mmu); > > } > > kvfree(kvm->arch.nested_mmus); > > kvm->arch.nested_mmus = NULL; > > -- > > 2.54.0.545.g6539524ca2-goog > > And here is the diff that should fix this patch. (Sorry!!) There are more issues. kvm->arch.mmu.split_page_cache can be freed by kvm_arch_commit_memory_region(), which holds slots_lock and slots_arch_lock, but not mmu_lock. IMO, the handling of kvm->arch.mmu.split_page_cache should be reworked. I don't entirely get the motivation for aggressively freeing the cache. The cache will only be filled if KVM actually does eager page splitting, so it's not like KVM is burning pages for setups that will never use the cache. Maybe I'm underestimating how many pages arm64 needs in the worst case scenario? (I can't follow the math, too many macros). But if KVM is configuring the cache with a capacity that's _so_ high that the "wasted" memory is problematic, then we probably should we revisit the capacity and algorithm. E.g. if KVM is splitting from 1GiB => 4KiB in a single pass (I can't tell if KVM does this on arm64), then we could break that into a 1GiB => 2MiB => 4KiB sequence. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-05 17:05 ` Sean Christopherson @ 2026-05-05 18:01 ` James Houghton 2026-05-05 18:16 ` Sean Christopherson 0 siblings, 1 reply; 12+ messages in thread From: James Houghton @ 2026-05-05 18:01 UTC (permalink / raw) To: Sean Christopherson Cc: chenhuacai, gshan, jhogan, joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-mips, loongarch, maobibo, maz, oupton, pbonzini, ricarkol, shahuang, stable, suzuki.poulose, yuzenghui, zhaotianrui On Tue, May 5, 2026 at 10:05 AM Sean Christopherson <seanjc@google.com> wrote: > > On Mon, May 04, 2026, James Houghton wrote: > > On Mon, May 4, 2026 at 3:42 PM James Houghton <jthoughton@google.com> wrote: > > > > > > kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` > > > concurrently in the event that the KVM's `mm` is __mmput() at the > > > same time that last reference to the KVM is being dropped. > > > > > > T1 T2 > > > KVM_CREATE_VM > > > Get VM file from T1 > > > close VM > > > exit_mm() close VM > > > > > > T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > > with only the KVM srcu read lock held. > > > > > > T2: kvm_vm_release() ---> mmu_notifier_unregister() -> > > > kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > > again, with only the KVM srcu read lock held. > > > > > > This leads to a potential double-free of > > > kvm->arch.kvm_mmu_free_memory_cache and now with NV > > > kvm->arch.nested_mmus. > > ... > > > > void kvm_uninit_stage2_mmu(struct kvm *kvm) > > > { > > > - kvm_free_stage2_pgd(&kvm->arch.mmu); > > > + lockdep_assert_held_write(&kvm->mmu_lock); > > > > *facepalm*.... this doesn't account for the other callers of > > kvm_uninit_stage2_mmu(). They will get lockdep warnings. > > > > I've attached a diff to the bottom of this reply that *does* deal with them. > > :( Sorry. > > ... > > > > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c > > > index 883b6c1008fb..977598bff5e6 100644 > > > --- a/arch/arm64/kvm/nested.c > > > +++ b/arch/arm64/kvm/nested.c > > > @@ -1190,11 +1190,13 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) > > > { > > > int i; > > > > > > + guard(write_lock)(&kvm->mmu_lock); > > > + > > > for (i = 0; i < kvm->arch.nested_mmus_size; i++) { > > > struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; > > > > > > if (!WARN_ON(atomic_read(&mmu->refcnt))) > > > - kvm_free_stage2_pgd(mmu); > > > + kvm_free_stage2_pgd_locked(mmu); > > > } > > > kvfree(kvm->arch.nested_mmus); > > > kvm->arch.nested_mmus = NULL; > > > -- > > > 2.54.0.545.g6539524ca2-goog > > > > And here is the diff that should fix this patch. (Sorry!!) > > There are more issues. kvm->arch.mmu.split_page_cache can be freed by > kvm_arch_commit_memory_region(), which holds slots_lock and slots_arch_lock, > but not mmu_lock. Thanks. I also noticed that kvm->arch.mmu.split_page_cache is documented as being protected by kvm->slots_lock; we should be holding it here. But we cannot take it here because we are already holding the KVM srcu lock. > IMO, the handling of kvm->arch.mmu.split_page_cache should be reworked. I don't > entirely get the motivation for aggressively freeing the cache. The cache will > only be filled if KVM actually does eager page splitting, so it's not like KVM is > burning pages for setups that will never use the cache. > > Maybe I'm underestimating how many pages arm64 needs in the worst case scenario? > (I can't follow the math, too many macros). But if KVM is configuring the cache > with a capacity that's _so_ high that the "wasted" memory is problematic, then we > probably should we revisit the capacity and algorithm. E.g. if KVM is splitting > from 1GiB => 4KiB in a single pass (I can't tell if KVM does this on arm64), then > we could break that into a 1GiB => 2MiB => 4KiB sequence. I'm not sure I've fully understood the point you're making, but I *think* we can just drop the kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); line from kvm_uninit_stage2_mmu(). It will get freed when the VM is destroyed anyway. So I'm thinking of splitting this patch into two (unless someone tells me otherwise): 1. Drop the kvm_mmu_free_memory_cache() from kvm_uninit_stage2_mmu() Fixes: e7bf7a490c68 ("KVM: arm64: Split huge pages when dirty logging is enabled") 2. Grab the MMU write lock around the kvfree(nested_mmus) bit in kvm_arch_flush_shadow_all(); do the kvfree() without holding the the lock. Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures") ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-05 18:01 ` James Houghton @ 2026-05-05 18:16 ` Sean Christopherson 2026-05-05 20:14 ` Sean Christopherson 0 siblings, 1 reply; 12+ messages in thread From: Sean Christopherson @ 2026-05-05 18:16 UTC (permalink / raw) To: James Houghton Cc: chenhuacai, gshan, jhogan, joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-mips, loongarch, maobibo, maz, oupton, pbonzini, ricarkol, shahuang, stable, suzuki.poulose, yuzenghui, zhaotianrui On Tue, May 05, 2026, James Houghton wrote: > On Tue, May 5, 2026 at 10:05 AM Sean Christopherson <seanjc@google.com> wrote: > > There are more issues. kvm->arch.mmu.split_page_cache can be freed by > > kvm_arch_commit_memory_region(), which holds slots_lock and slots_arch_lock, > > but not mmu_lock. > > Thanks. I also noticed that kvm->arch.mmu.split_page_cache is > documented as being protected by kvm->slots_lock; we should be holding > it here. But we cannot take it here because we are already holding the > KVM srcu lock. > > > IMO, the handling of kvm->arch.mmu.split_page_cache should be reworked. I don't > > entirely get the motivation for aggressively freeing the cache. The cache will > > only be filled if KVM actually does eager page splitting, so it's not like KVM is > > burning pages for setups that will never use the cache. > > > > Maybe I'm underestimating how many pages arm64 needs in the worst case scenario? > > (I can't follow the math, too many macros). But if KVM is configuring the cache > > with a capacity that's _so_ high that the "wasted" memory is problematic, then we > > probably should we revisit the capacity and algorithm. E.g. if KVM is splitting > > from 1GiB => 4KiB in a single pass (I can't tell if KVM does this on arm64), then > > we could break that into a 1GiB => 2MiB => 4KiB sequence. > > I'm not sure I've fully understood the point you're making, but I > *think* we can just drop the > kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); > line from kvm_uninit_stage2_mmu(). It will get freed when the VM is > destroyed anyway. It's not that simple. KVM arm64 allows userspace to reconfigure the capacity of the cache via KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE. kvm_vm_ioctl_enable_cap() currently allows userspace to do that so long as there are no memslots. __kvm_mmu_topup_memory_cache() will (rightly) yell and fail if it's called with the "wrong" capacity, so we'd need to sort that out. The other issue is that it's not clear to me what happens for large "chunk" sizes. If KVM is splitting from 1GiB (or whatever huge-hugepage sizes are supported on arm64) all the way to 4KiB, e.g. to optimize against break-before-make, then the capacity of the cache could be significant, e.g. MiB of memory or worse. My read of things is that purging the cache when dirty logging is disabled is a guard against consuming too much memory when the chunk size is large. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-05 18:16 ` Sean Christopherson @ 2026-05-05 20:14 ` Sean Christopherson 0 siblings, 0 replies; 12+ messages in thread From: Sean Christopherson @ 2026-05-05 20:14 UTC (permalink / raw) To: James Houghton Cc: chenhuacai, gshan, jhogan, joey.gouly, kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-mips, loongarch, maobibo, maz, oupton, pbonzini, ricarkol, shahuang, stable, suzuki.poulose, yuzenghui, zhaotianrui On Tue, May 05, 2026, Sean Christopherson wrote: > On Tue, May 05, 2026, James Houghton wrote: > > On Tue, May 5, 2026 at 10:05 AM Sean Christopherson <seanjc@google.com> wrote: > > > There are more issues. kvm->arch.mmu.split_page_cache can be freed by > > > kvm_arch_commit_memory_region(), which holds slots_lock and slots_arch_lock, > > > but not mmu_lock. > > > > Thanks. I also noticed that kvm->arch.mmu.split_page_cache is > > documented as being protected by kvm->slots_lock; we should be holding > > it here. But we cannot take it here because we are already holding the > > KVM srcu lock. > > > > > IMO, the handling of kvm->arch.mmu.split_page_cache should be reworked. I don't > > > entirely get the motivation for aggressively freeing the cache. The cache will > > > only be filled if KVM actually does eager page splitting, so it's not like KVM is > > > burning pages for setups that will never use the cache. > > > > > > Maybe I'm underestimating how many pages arm64 needs in the worst case scenario? > > > (I can't follow the math, too many macros). But if KVM is configuring the cache > > > with a capacity that's _so_ high that the "wasted" memory is problematic, then we > > > probably should we revisit the capacity and algorithm. E.g. if KVM is splitting > > > from 1GiB => 4KiB in a single pass (I can't tell if KVM does this on arm64), then > > > we could break that into a 1GiB => 2MiB => 4KiB sequence. > > > > I'm not sure I've fully understood the point you're making, but I > > *think* we can just drop the > > kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); > > line from kvm_uninit_stage2_mmu(). It will get freed when the VM is > > destroyed anyway. > > It's not that simple. KVM arm64 allows userspace to reconfigure the capacity of > the cache via KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE. kvm_vm_ioctl_enable_cap() > currently allows userspace to do that so long as there are no memslots. > __kvm_mmu_topup_memory_cache() will (rightly) yell and fail if it's called with > the "wrong" capacity, so we'd need to sort that out. > > The other issue is that it's not clear to me what happens for large "chunk" sizes. > If KVM is splitting from 1GiB (or whatever huge-hugepage sizes are supported on > arm64) all the way to 4KiB, e.g. to optimize against break-before-make, then the > capacity of the cache could be significant, e.g. MiB of memory or worse. My read > of things is that purging the cache when dirty logging is disabled is a guard > against consuming too much memory when the chunk size is large. If we go down the "let's simplify things" path, one way to simplify the handling of KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE would be to disallow changing the chunk size once the cache has been used. That would allow deferring teardown of the cache until VM destruction. diff --git arch/arm64/kvm/arm.c arch/arm64/kvm/arm.c index 176cbe8baad3..671f7ac5e31d 100644 --- arch/arm64/kvm/arm.c +++ arch/arm64/kvm/arm.c @@ -165,7 +165,8 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, * To keep things simple, allow changing the chunk * size only when no memory slots have been created. */ - if (kvm_are_all_memslots_empty(kvm)) { + if (kvm_are_all_memslots_empty(kvm) && + !kvm->arch.mmu.split_page_cache.capacity) { u64 new_cap = cap->args[0]; if (!new_cap || kvm_is_block_size_supported(new_cap)) { ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-04 22:42 ` [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() James Houghton 2026-05-04 23:10 ` James Houghton @ 2026-05-06 2:27 ` Bibo Mao 2026-05-06 13:55 ` Sean Christopherson 1 sibling, 1 reply; 12+ messages in thread From: Bibo Mao @ 2026-05-06 2:27 UTC (permalink / raw) To: James Houghton, Paolo Bonzini Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Sean Christopherson, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, stable On 2026/5/5 上午6:42, James Houghton wrote: > kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` > concurrently in the event that the KVM's `mm` is __mmput() at the > same time that last reference to the KVM is being dropped. > > T1 T2 > KVM_CREATE_VM > Get VM file from T1 > close VM > exit_mm() close VM > > T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > with only the KVM srcu read lock held. > > T2: kvm_vm_release() ---> mmu_notifier_unregister() -> > kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > again, with only the KVM srcu read lock held. By looking through the code, kvm_arch_destroy_vm() will free PGD page only, page table walking is executing in deleting memslot or exit_mm(). With normal code, life cycle of VM is something like this: KVM_CREATE_VM Create_VCPUs Create memslots Destroy_VCPUs Destroy memslots close VM exit_mm() And there is kvm_get_kvm()/kvm_put_kvm() function call with creating/destroy vCPUs, however no such operations with memslot operation. Is it possible that VM is destroyed without removing memslots, such as the following operation. KVM_CREATE_VM Create memslots close VM exit_mm() Regards Bibo Mao > > This leads to a potential double-free of > kvm->arch.kvm_mmu_free_memory_cache and now with NV > kvm->arch.nested_mmus. > > Cc: stable@vger.kernel.org > Fixes: e7bf7a490c68 ("KVM: arm64: Split huge pages when dirty logging is enabled") > Signed-off-by: James Houghton <jthoughton@google.com> > --- > arch/arm64/include/asm/kvm_mmu.h | 1 + > arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++---- > arch/arm64/kvm/nested.c | 4 +++- > 3 files changed, 23 insertions(+), 5 deletions(-) > > diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h > index 01e9c72d6aa7..30d5c24fcebb 100644 > --- a/arch/arm64/include/asm/kvm_mmu.h > +++ b/arch/arm64/include/asm/kvm_mmu.h > @@ -178,6 +178,7 @@ void stage2_unmap_vm(struct kvm *kvm); > int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long type); > void kvm_uninit_stage2_mmu(struct kvm *kvm); > void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu); > +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu); > int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, > phys_addr_t pa, unsigned long size, bool writable); > > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c > index d089c107d9b7..4bab407d43bb 100644 > --- a/arch/arm64/kvm/mmu.c > +++ b/arch/arm64/kvm/mmu.c > @@ -1021,7 +1021,9 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t > > void kvm_uninit_stage2_mmu(struct kvm *kvm) > { > - kvm_free_stage2_pgd(&kvm->arch.mmu); > + lockdep_assert_held_write(&kvm->mmu_lock); > + > + kvm_free_stage2_pgd_locked(&kvm->arch.mmu); > kvm_mmu_free_memory_cache(&kvm->arch.mmu.split_page_cache); > } > > @@ -1095,12 +1097,14 @@ void stage2_unmap_vm(struct kvm *kvm) > srcu_read_unlock(&kvm->srcu, idx); > } > > -void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > +static void __kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu, bool locked) > { > struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); > struct kvm_pgtable *pgt = NULL; > > - write_lock(&kvm->mmu_lock); > + if (!locked) > + write_lock(&kvm->mmu_lock); > + > pgt = mmu->pgt; > if (pgt) { > mmu->pgd_phys = 0; > @@ -1111,7 +1115,8 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > if (kvm_is_nested_s2_mmu(kvm, mmu)) > kvm_init_nested_s2_mmu(mmu); > > - write_unlock(&kvm->mmu_lock); > + if (!locked) > + write_unlock(&kvm->mmu_lock); > > if (pgt) { > kvm_stage2_destroy(pgt); > @@ -1119,6 +1124,16 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > } > } > > +void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu) > +{ > + __kvm_free_stage2_pgd(mmu, false); > +} > + > +void kvm_free_stage2_pgd_locked(struct kvm_s2_mmu *mmu) > +{ > + __kvm_free_stage2_pgd(mmu, true); > +} > + > static void hyp_mc_free_fn(void *addr, void *mc) > { > struct kvm_hyp_memcache *memcache = mc; > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c > index 883b6c1008fb..977598bff5e6 100644 > --- a/arch/arm64/kvm/nested.c > +++ b/arch/arm64/kvm/nested.c > @@ -1190,11 +1190,13 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) > { > int i; > > + guard(write_lock)(&kvm->mmu_lock); > + > for (i = 0; i < kvm->arch.nested_mmus_size; i++) { > struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; > > if (!WARN_ON(atomic_read(&mmu->refcnt))) > - kvm_free_stage2_pgd(mmu); > + kvm_free_stage2_pgd_locked(mmu); > } > kvfree(kvm->arch.nested_mmus); > kvm->arch.nested_mmus = NULL; > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-06 2:27 ` Bibo Mao @ 2026-05-06 13:55 ` Sean Christopherson 2026-05-07 1:40 ` Bibo Mao 0 siblings, 1 reply; 12+ messages in thread From: Sean Christopherson @ 2026-05-06 13:55 UTC (permalink / raw) To: Bibo Mao Cc: James Houghton, Paolo Bonzini, Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, stable On Wed, May 06, 2026, Bibo Mao wrote: > On 2026/5/5 上午6:42, James Houghton wrote: > > kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` > > concurrently in the event that the KVM's `mm` is __mmput() at the > > same time that last reference to the KVM is being dropped. > > > > T1 T2 > > KVM_CREATE_VM > > Get VM file from T1 > > close VM > > exit_mm() close VM > > > > T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > with only the KVM srcu read lock held. > > > > T2: kvm_vm_release() ---> mmu_notifier_unregister() -> > > kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), > > again, with only the KVM srcu read lock held. > By looking through the code, kvm_arch_destroy_vm() will free PGD page only, > page table walking is executing in deleting memslot or exit_mm(). > > With normal code, life cycle of VM is something like this: Not necessarily. Abruptly closing the VM, as described below, is also "normal" (though likely uncommon). > KVM_CREATE_VM > Create_VCPUs > Create memslots > Destroy_VCPUs This is incorrect. KVM doesn't provide any way for userspace to destroy vCPUs. Userspace can fully release every vCPU fd, but the vCPU object within KVM stays alive (and indirectly reachable) until the VM is destroyed. > Destroy memslots > close VM > exit_mm() Note, exit_mm() may or may not be called. E.g. there are VMMs that will destroy a VM and start a new one (perhaps even the same conceptual virtual machine) in the same process / mm_struct / address space. > And there is kvm_get_kvm()/kvm_put_kvm() function call with creating/destroy > vCPUs, however no such operations with memslot operation. Is it possible > that VM is destroyed without removing memslots, such as the following > operation. > KVM_CREATE_VM > Create memslots > close VM > exit_mm() Yep. KVM cannot make any assumptions when it comes to userspace-initiated operations. Even a VMM that super strictly follows the first approach may exit abruptly, without destroying memslots, e.g. if it's OOM-killed. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() 2026-05-06 13:55 ` Sean Christopherson @ 2026-05-07 1:40 ` Bibo Mao 0 siblings, 0 replies; 12+ messages in thread From: Bibo Mao @ 2026-05-07 1:40 UTC (permalink / raw) To: Sean Christopherson Cc: James Houghton, Paolo Bonzini, Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, stable On 2026/5/6 下午9:55, Sean Christopherson wrote: > On Wed, May 06, 2026, Bibo Mao wrote: >> On 2026/5/5 上午6:42, James Houghton wrote: >>> kvm_arch_flush_shadow_all() may sometimes be called on the same `kvm` >>> concurrently in the event that the KVM's `mm` is __mmput() at the >>> same time that last reference to the KVM is being dropped. >>> >>> T1 T2 >>> KVM_CREATE_VM >>> Get VM file from T1 >>> close VM >>> exit_mm() close VM >>> >>> T1: exit_mm() -> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), >>> with only the KVM srcu read lock held. >>> >>> T2: kvm_vm_release() ---> mmu_notifier_unregister() -> >>> kvm_mmu_notifier_release() -> kvm_flush_shadow_all(), >>> again, with only the KVM srcu read lock held. >> By looking through the code, kvm_arch_destroy_vm() will free PGD page only, >> page table walking is executing in deleting memslot or exit_mm(). >> >> With normal code, life cycle of VM is something like this: > > Not necessarily. Abruptly closing the VM, as described below, is also "normal" > (though likely uncommon). > >> KVM_CREATE_VM >> Create_VCPUs >> Create memslots >> Destroy_VCPUs > > This is incorrect. KVM doesn't provide any way for userspace to destroy vCPUs. > Userspace can fully release every vCPU fd, but the vCPU object within KVM stays > alive (and indirectly reachable) until the VM is destroyed. yes, that is so. For users, there is vCPU fd release interface. As for vCPU object release, it is kind of internal implementation in KVM. > >> Destroy memslots >> close VM >> exit_mm() > > Note, exit_mm() may or may not be called. E.g. there are VMMs that will destroy > a VM and start a new one (perhaps even the same conceptual virtual machine) in > the same process / mm_struct / address space. > >> And there is kvm_get_kvm()/kvm_put_kvm() function call with creating/destroy >> vCPUs, however no such operations with memslot operation. Is it possible >> that VM is destroyed without removing memslots, such as the following >> operation. >> KVM_CREATE_VM >> Create memslots >> close VM >> exit_mm() > > Yep. KVM cannot make any assumptions when it comes to userspace-initiated > operations. Even a VMM that super strictly follows the first approach may exit > abruptly, without destroying memslots, e.g. if it's OOM-killed. oh, I see. If so, the implementation about kvm_arch_destroy_vm() on Loongarch is problematic, it needs consider this kind of abnormal VM destroy, take page table walk and free page tables. I had thought that memslots create/destroy should call kvm_get_kvm()/kvm_put_kvm() :( Regards Bibo Mao ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/5] KVM: loongarch: Grab MMU lock in kvm_arch_flush_shadow_all() [not found] <20260504224213.1049426-1-jthoughton@google.com> 2026-05-04 22:42 ` [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() James Houghton @ 2026-05-04 22:42 ` James Houghton 2026-05-08 7:21 ` Bibo Mao 2026-05-04 22:42 ` [PATCH 3/5] KVM: mips: " James Houghton 2 siblings, 1 reply; 12+ messages in thread From: James Houghton @ 2026-05-04 22:42 UTC (permalink / raw) To: Paolo Bonzini Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Sean Christopherson, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Bibo Mao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, James Houghton, stable kvm_arch_flush_shadow_all() may be called concurrently on the same `kvm`. This could at least result in accounting mistakes (e.g. underflows on `kvm->stat.*pages`). Cc: stable@vger.kernel.org Fixes: 752e2cd7b4fb ("LoongArch: KVM: Implement kvm mmu operations") Signed-off-by: James Houghton <jthoughton@google.com> --- Note: This is compile-tested only! arch/loongarch/kvm/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index a7fa458e3360..5dbce9b18e1c 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -486,7 +486,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, void kvm_arch_flush_shadow_all(struct kvm *kvm) { - kvm_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT, 0); + kvm_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT, 1); } void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] KVM: loongarch: Grab MMU lock in kvm_arch_flush_shadow_all() 2026-05-04 22:42 ` [PATCH 2/5] KVM: loongarch: Grab MMU " James Houghton @ 2026-05-08 7:21 ` Bibo Mao 0 siblings, 0 replies; 12+ messages in thread From: Bibo Mao @ 2026-05-08 7:21 UTC (permalink / raw) To: James Houghton, Paolo Bonzini Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Sean Christopherson, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, stable On 2026/5/5 上午6:42, James Houghton wrote: > kvm_arch_flush_shadow_all() may be called concurrently on the same > `kvm`. This could at least result in accounting mistakes (e.g. > underflows on `kvm->stat.*pages`). > > Cc: stable@vger.kernel.org > Fixes: 752e2cd7b4fb ("LoongArch: KVM: Implement kvm mmu operations") > Signed-off-by: James Houghton <jthoughton@google.com> > --- > Note: This is compile-tested only! > > arch/loongarch/kvm/mmu.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c > index a7fa458e3360..5dbce9b18e1c 100644 > --- a/arch/loongarch/kvm/mmu.c > +++ b/arch/loongarch/kvm/mmu.c > @@ -486,7 +486,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, > > void kvm_arch_flush_shadow_all(struct kvm *kvm) > { > - kvm_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT, 0); > + kvm_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT, 1); > } > > void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) > Reviewed-by: Bibo Mao <maobibo@loongson.cn> ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/5] KVM: mips: Grab MMU lock in kvm_arch_flush_shadow_all() [not found] <20260504224213.1049426-1-jthoughton@google.com> 2026-05-04 22:42 ` [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() James Houghton 2026-05-04 22:42 ` [PATCH 2/5] KVM: loongarch: Grab MMU " James Houghton @ 2026-05-04 22:42 ` James Houghton 2 siblings, 0 replies; 12+ messages in thread From: James Houghton @ 2026-05-04 22:42 UTC (permalink / raw) To: Paolo Bonzini Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu, Sean Christopherson, Gavin Shan, Shaoqin Huang, Ricardo Koller, Tianrui Zhao, Bibo Mao, Huacai Chen, James Hogan, linux-arm-kernel, kvmarm, loongarch, linux-mips, kvm, linux-kernel, James Houghton, stable kvm_mips_flush_gpa_pt() expects the MMU lock to be held; it is not in this path. This can lead to a double-free of page table entries if kvm_arch_flush_shadow_all() is called twice on the same `kvm` concurrently; such a scenario is possible. Cc: stable@vger.kernel.org Fixes: b62091108633 ("KVM: MIPS: Implement kvm_arch_flush_shadow_all/memslot") Signed-off-by: James Houghton <jthoughton@google.com> --- Note: This is compile-tested only! arch/mips/kvm/mips.c | 2 ++ arch/mips/kvm/mmu.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index a53abbba43ea..463b6c4aa62c 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -180,6 +180,8 @@ long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, void kvm_arch_flush_shadow_all(struct kvm *kvm) { + guard(spinlock)(&kvm->mmu_lock); + /* Flush whole GPA */ kvm_mips_flush_gpa_pt(kvm, 0, ~0); kvm_flush_remote_tlbs(kvm); diff --git a/arch/mips/kvm/mmu.c b/arch/mips/kvm/mmu.c index d2c3b6b41f18..5045833f8116 100644 --- a/arch/mips/kvm/mmu.c +++ b/arch/mips/kvm/mmu.c @@ -269,6 +269,8 @@ static bool kvm_mips_flush_gpa_pgd(pgd_t *pgd, unsigned long start_gpa, */ bool kvm_mips_flush_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn) { + lockdep_assert_held(&kvm->mmu_lock); + return kvm_mips_flush_gpa_pgd(kvm->arch.gpa_mm.pgd, start_gfn << PAGE_SHIFT, end_gfn << PAGE_SHIFT); -- 2.54.0.545.g6539524ca2-goog ^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-05-08 7:24 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260504224213.1049426-1-jthoughton@google.com>
2026-05-04 22:42 ` [PATCH 1/5] KVM: arm64: Grab KVM MMU write lock in kvm_arch_flush_shadow_all() James Houghton
2026-05-04 23:10 ` James Houghton
2026-05-05 17:05 ` Sean Christopherson
2026-05-05 18:01 ` James Houghton
2026-05-05 18:16 ` Sean Christopherson
2026-05-05 20:14 ` Sean Christopherson
2026-05-06 2:27 ` Bibo Mao
2026-05-06 13:55 ` Sean Christopherson
2026-05-07 1:40 ` Bibo Mao
2026-05-04 22:42 ` [PATCH 2/5] KVM: loongarch: Grab MMU " James Houghton
2026-05-08 7:21 ` Bibo Mao
2026-05-04 22:42 ` [PATCH 3/5] KVM: mips: " James Houghton
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox