Re: [PATCH v4 21/30] KVM: x86/mmu: Zap invalidated roots via asynchronous worker

From: Sean Christopherson
Date: Thu Mar 03 2022 - 15:54:51 EST


On Thu, Mar 03, 2022, Paolo Bonzini wrote:
> diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
> index 0b88592495f8..9287ee078c49 100644
> --- a/arch/x86/kvm/mmu/mmu.c
> +++ b/arch/x86/kvm/mmu/mmu.c
> @@ -5730,7 +5730,6 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm)
> kvm_make_all_cpus_request(kvm, KVM_REQ_MMU_FREE_OBSOLETE_ROOTS);
>
> kvm_zap_obsolete_pages(kvm);
> -

Spurious whitespace deletion.

> write_unlock(&kvm->mmu_lock);
>
> /*
> @@ -5741,11 +5740,8 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm)
> * Deferring the zap until the final reference to the root is put would
> * lead to use-after-free.
> */
> - if (is_tdp_mmu_enabled(kvm)) {
> - read_lock(&kvm->mmu_lock);
> + if (is_tdp_mmu_enabled(kvm))
> kvm_tdp_mmu_zap_invalidated_roots(kvm);
> - read_unlock(&kvm->mmu_lock);
> - }
> }
>
> static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm)

...

> +static void tdp_mmu_schedule_zap_root(struct kvm *kvm, struct kvm_mmu_page *root)
> +{

Definitely worth doing (I'll provide more info in the "Zap defunct roots" patch):

WARN_ON_ONCE(!root->role.invalid || root->tdp_mmu_async_data);

The assertion on role.invalid is a little overkill, but might help document when
and how this is used.

> + root->tdp_mmu_async_data = kvm;
> + INIT_WORK(&root->tdp_mmu_async_work, tdp_mmu_zap_root_work);
> + queue_work(kvm->arch.tdp_mmu_zap_wq, &root->tdp_mmu_async_work);
> +}
> +
> +static inline bool kvm_tdp_root_mark_invalid(struct kvm_mmu_page *page)
> +{
> + union kvm_mmu_page_role role = page->role;
> + role.invalid = true;
> +
> + /* No need to use cmpxchg, only the invalid bit can change. */
> + role.word = xchg(&page->role.word, role.word);
> + return role.invalid;

This helper is unused. It _could_ be used here, but I think it belongs in the
next patch. Critically, until zapping defunct roots creates the invariant that
invalid roots are _always_ zapped via worker, kvm_tdp_mmu_invalidate_all_roots()
must not assume that an invalid root is queued for zapping. I.e. doing this
before the "Zap defunct roots" would be wrong:

list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link) {
if (kvm_tdp_root_mark_invalid(root))
continue;

if (WARN_ON_ONCE(!kvm_tdp_mmu_get_root(root)));
continue;

tdp_mmu_schedule_zap_root(kvm, root);
}