On Fri, Apr 21, 2023 at 2:49 PM Sean Christopherson <seanjc@xxxxxxxxxx> wrote: > > void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm) > { > struct kvm_mmu_page *root; > > - lockdep_assert_held_write(&kvm->mmu_lock); > - list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link) { > - if (!root->role.invalid && > - !WARN_ON_ONCE(!kvm_tdp_mmu_get_root(root))) { > + /* > + * Note! mmu_lock isn't held when destroying the VM! There can't be > + * other references to @kvm, i.e. nothing else can invalidate roots, > + * but walking the list of roots does need to be guarded against roots > + * being deleted by the asynchronous zap worker. > + */ > + rcu_read_lock(); > + > + list_for_each_entry_rcu(root, &kvm->arch.tdp_mmu_roots, link) { I see that roots are removed from the list with list_del_rcu(), so I agree this should be safe. KVM could, alternatively, acquire the mmu_lock in kvm_mmu_uninit_tdp_mmu(), which would let us keep the lockdep assertion and drop the rcu_read_lock() + comment. That might be worth it in case someone accidentally adds a call to kvm_tdp_mmu_invalidate_all_roots() without mmu_lock outside of VM teardown. kvm_mmu_uninit_tdp_mmu() is not a particularly performance sensitive path and adding the mmu_lock wouldn't add much overhead anyway (it would block for at most a few milliseconds waiting for the async work to reschedule).