KVM: x86/mmu: Tear down roots before kvm_mmu_zap_all_fast returns
To avoid saddling a vCPU thread with the work of tearing down an entire paging structure, take a reference on each root before they become obsolete, so that the thread initiating the fast invalidation can tear down the paging structure and (most likely) release the last reference. As a bonus, this teardown can happen under the MMU lock in read mode so as not to block the progress of vCPU threads. Signed-off-by: Ben Gardon <bgardon@google.com> Message-Id: <20210401233736.638171-14-bgardon@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
b7cccd397f
commit
4c6654bd16
@ -5443,6 +5443,12 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm)
|
|||||||
kvm_zap_obsolete_pages(kvm);
|
kvm_zap_obsolete_pages(kvm);
|
||||||
|
|
||||||
write_unlock(&kvm->mmu_lock);
|
write_unlock(&kvm->mmu_lock);
|
||||||
|
|
||||||
|
if (is_tdp_mmu_enabled(kvm)) {
|
||||||
|
read_lock(&kvm->mmu_lock);
|
||||||
|
kvm_tdp_mmu_zap_invalidated_roots(kvm);
|
||||||
|
read_unlock(&kvm->mmu_lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm)
|
static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm)
|
||||||
|
@ -797,11 +797,91 @@ void kvm_tdp_mmu_zap_all(struct kvm *kvm)
|
|||||||
kvm_flush_remote_tlbs(kvm);
|
kvm_flush_remote_tlbs(kvm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct kvm_mmu_page *next_invalidated_root(struct kvm *kvm,
|
||||||
|
struct kvm_mmu_page *prev_root)
|
||||||
|
{
|
||||||
|
struct kvm_mmu_page *next_root;
|
||||||
|
|
||||||
|
if (prev_root)
|
||||||
|
next_root = list_next_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
||||||
|
&prev_root->link,
|
||||||
|
typeof(*prev_root), link);
|
||||||
|
else
|
||||||
|
next_root = list_first_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
||||||
|
typeof(*next_root), link);
|
||||||
|
|
||||||
|
while (next_root && !(next_root->role.invalid &&
|
||||||
|
refcount_read(&next_root->tdp_mmu_root_count)))
|
||||||
|
next_root = list_next_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
||||||
|
&next_root->link,
|
||||||
|
typeof(*next_root), link);
|
||||||
|
|
||||||
|
return next_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since kvm_tdp_mmu_zap_all_fast has acquired a reference to each
|
||||||
|
* invalidated root, they will not be freed until this function drops the
|
||||||
|
* reference. Before dropping that reference, tear down the paging
|
||||||
|
* structure so that whichever thread does drop the last reference
|
||||||
|
* only has to do a trivial amount of work. Since the roots are invalid,
|
||||||
|
* no new SPTEs should be created under them.
|
||||||
|
*/
|
||||||
|
void kvm_tdp_mmu_zap_invalidated_roots(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
gfn_t max_gfn = 1ULL << (shadow_phys_bits - PAGE_SHIFT);
|
||||||
|
struct kvm_mmu_page *next_root;
|
||||||
|
struct kvm_mmu_page *root;
|
||||||
|
bool flush = false;
|
||||||
|
|
||||||
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
root = next_invalidated_root(kvm, NULL);
|
||||||
|
|
||||||
|
while (root) {
|
||||||
|
next_root = next_invalidated_root(kvm, root);
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
flush = zap_gfn_range(kvm, root, 0, max_gfn, true, flush,
|
||||||
|
true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put the reference acquired in
|
||||||
|
* kvm_tdp_mmu_invalidate_roots
|
||||||
|
*/
|
||||||
|
kvm_tdp_mmu_put_root(kvm, root, true);
|
||||||
|
|
||||||
|
root = next_root;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (flush)
|
||||||
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark each TDP MMU root as invalid so that other threads
|
* Mark each TDP MMU root as invalid so that other threads
|
||||||
* will drop their references and allow the root count to
|
* will drop their references and allow the root count to
|
||||||
* go to 0.
|
* go to 0.
|
||||||
*
|
*
|
||||||
|
* Also take a reference on all roots so that this thread
|
||||||
|
* can do the bulk of the work required to free the roots
|
||||||
|
* once they are invalidated. Without this reference, a
|
||||||
|
* vCPU thread might drop the last reference to a root and
|
||||||
|
* get stuck with tearing down the entire paging structure.
|
||||||
|
*
|
||||||
|
* Roots which have a zero refcount should be skipped as
|
||||||
|
* they're already being torn down.
|
||||||
|
* Already invalid roots should be referenced again so that
|
||||||
|
* they aren't freed before kvm_tdp_mmu_zap_all_fast is
|
||||||
|
* done with them.
|
||||||
|
*
|
||||||
* This has essentially the same effect for the TDP MMU
|
* This has essentially the same effect for the TDP MMU
|
||||||
* as updating mmu_valid_gen does for the shadow MMU.
|
* as updating mmu_valid_gen does for the shadow MMU.
|
||||||
*/
|
*/
|
||||||
@ -811,7 +891,8 @@ void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm)
|
|||||||
|
|
||||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||||
list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link)
|
list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link)
|
||||||
root->role.invalid = true;
|
if (refcount_inc_not_zero(&root->tdp_mmu_root_count))
|
||||||
|
root->role.invalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,6 +49,7 @@ static inline bool kvm_tdp_mmu_zap_sp(struct kvm *kvm, struct kvm_mmu_page *sp)
|
|||||||
|
|
||||||
void kvm_tdp_mmu_zap_all(struct kvm *kvm);
|
void kvm_tdp_mmu_zap_all(struct kvm *kvm);
|
||||||
void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm);
|
void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm);
|
||||||
|
void kvm_tdp_mmu_zap_invalidated_roots(struct kvm *kvm);
|
||||||
|
|
||||||
int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,
|
int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,
|
||||||
int map_writable, int max_level, kvm_pfn_t pfn,
|
int map_writable, int max_level, kvm_pfn_t pfn,
|
||||||
|
Loading…
Reference in New Issue
Block a user