2020-10-15 02:26:43 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
2020-10-15 02:26:44 +08:00
|
|
|
#include "mmu.h"
|
|
|
|
#include "mmu_internal.h"
|
2020-10-15 02:26:50 +08:00
|
|
|
#include "mmutrace.h"
|
2020-10-15 02:26:45 +08:00
|
|
|
#include "tdp_iter.h"
|
2020-10-15 02:26:43 +08:00
|
|
|
#include "tdp_mmu.h"
|
2020-10-15 02:26:44 +08:00
|
|
|
#include "spte.h"
|
2020-10-15 02:26:43 +08:00
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
#include <asm/cmpxchg.h>
|
2020-10-28 01:59:43 +08:00
|
|
|
#include <trace/events/kvm.h>
|
|
|
|
|
2020-10-15 02:26:43 +08:00
|
|
|
static bool __read_mostly tdp_mmu_enabled = false;
|
2020-10-15 02:26:58 +08:00
|
|
|
module_param_named(tdp_mmu, tdp_mmu_enabled, bool, 0644);
|
2020-10-15 02:26:43 +08:00
|
|
|
|
|
|
|
/* Initializes the TDP MMU for the VM, if enabled. */
|
|
|
|
void kvm_mmu_init_tdp_mmu(struct kvm *kvm)
|
|
|
|
{
|
2021-02-06 22:53:33 +08:00
|
|
|
if (!tdp_enabled || !READ_ONCE(tdp_mmu_enabled))
|
2020-10-15 02:26:43 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* This should not be changed for the lifetime of the VM. */
|
|
|
|
kvm->arch.tdp_mmu_enabled = true;
|
2020-10-15 02:26:44 +08:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&kvm->arch.tdp_mmu_roots);
|
2021-02-03 02:57:26 +08:00
|
|
|
spin_lock_init(&kvm->arch.tdp_mmu_pages_lock);
|
2020-10-15 02:26:51 +08:00
|
|
|
INIT_LIST_HEAD(&kvm->arch.tdp_mmu_pages);
|
2020-10-15 02:26:43 +08:00
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
static __always_inline void kvm_lockdep_assert_mmu_lock_held(struct kvm *kvm,
|
|
|
|
bool shared)
|
|
|
|
{
|
|
|
|
if (shared)
|
|
|
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
|
|
|
else
|
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:43 +08:00
|
|
|
void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
if (!kvm->arch.tdp_mmu_enabled)
|
|
|
|
return;
|
2020-10-15 02:26:44 +08:00
|
|
|
|
|
|
|
WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots));
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that all the outstanding RCU callbacks to free shadow pages
|
|
|
|
* can run before the VM is torn down.
|
|
|
|
*/
|
|
|
|
rcu_barrier();
|
2020-10-15 02:26:44 +08:00
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:27 +08:00
|
|
|
static bool zap_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
|
2021-04-02 07:37:32 +08:00
|
|
|
gfn_t start, gfn_t end, bool can_yield, bool flush,
|
|
|
|
bool shared);
|
2021-04-02 07:37:27 +08:00
|
|
|
|
|
|
|
static void tdp_mmu_free_sp(struct kvm_mmu_page *sp)
|
2021-01-07 08:19:34 +08:00
|
|
|
{
|
2021-04-02 07:37:27 +08:00
|
|
|
free_page((unsigned long)sp->spt);
|
|
|
|
kmem_cache_free(mmu_page_header_cache, sp);
|
2021-01-07 08:19:34 +08:00
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
/*
|
|
|
|
* This is called through call_rcu in order to free TDP page table memory
|
|
|
|
* safely with respect to other kernel threads that may be operating on
|
|
|
|
* the memory.
|
|
|
|
* By only accessing TDP MMU page table memory in an RCU read critical
|
|
|
|
* section, and freeing it after a grace period, lockless access to that
|
|
|
|
* memory won't use it after it is freed.
|
|
|
|
*/
|
|
|
|
static void tdp_mmu_free_sp_rcu_callback(struct rcu_head *head)
|
2021-01-07 08:19:34 +08:00
|
|
|
{
|
2021-04-02 07:37:31 +08:00
|
|
|
struct kvm_mmu_page *sp = container_of(head, struct kvm_mmu_page,
|
|
|
|
rcu_head);
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
tdp_mmu_free_sp(sp);
|
|
|
|
}
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
void kvm_tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root,
|
|
|
|
bool shared)
|
2021-04-02 07:37:27 +08:00
|
|
|
{
|
|
|
|
gfn_t max_gfn = 1ULL << (shadow_phys_bits - PAGE_SHIFT);
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
kvm_lockdep_assert_mmu_lock_held(kvm, shared);
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:29 +08:00
|
|
|
if (!refcount_dec_and_test(&root->tdp_mmu_root_count))
|
2021-04-02 07:37:27 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
WARN_ON(!root->tdp_mmu_page);
|
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
spin_lock(&kvm->arch.tdp_mmu_pages_lock);
|
|
|
|
list_del_rcu(&root->link);
|
|
|
|
spin_unlock(&kvm->arch.tdp_mmu_pages_lock);
|
2021-04-02 07:37:27 +08:00
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
zap_gfn_range(kvm, root, 0, max_gfn, false, false, shared);
|
2021-04-02 07:37:27 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
call_rcu(&root->rcu_head, tdp_mmu_free_sp_rcu_callback);
|
2021-01-07 08:19:34 +08:00
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:28 +08:00
|
|
|
/*
|
|
|
|
* Finds the next valid root after root (or the first valid root if root
|
|
|
|
* is NULL), takes a reference on it, and returns that next root. If root
|
|
|
|
* is not NULL, this thread should have already taken a reference on it, and
|
|
|
|
* that reference will be dropped. If no valid root is found, this
|
|
|
|
* function will return NULL.
|
|
|
|
*/
|
|
|
|
static struct kvm_mmu_page *tdp_mmu_next_root(struct kvm *kvm,
|
2021-04-02 07:37:32 +08:00
|
|
|
struct kvm_mmu_page *prev_root,
|
|
|
|
bool shared)
|
2021-01-07 08:19:34 +08:00
|
|
|
{
|
|
|
|
struct kvm_mmu_page *next_root;
|
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2021-04-02 07:37:28 +08:00
|
|
|
if (prev_root)
|
2021-04-02 07:37:31 +08:00
|
|
|
next_root = list_next_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
|
|
|
&prev_root->link,
|
|
|
|
typeof(*prev_root), link);
|
2021-04-02 07:37:28 +08:00
|
|
|
else
|
2021-04-02 07:37:31 +08:00
|
|
|
next_root = list_first_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
|
|
|
typeof(*next_root), link);
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
while (next_root && !kvm_tdp_mmu_get_root(kvm, next_root))
|
|
|
|
next_root = list_next_or_null_rcu(&kvm->arch.tdp_mmu_roots,
|
|
|
|
&next_root->link, typeof(*next_root), link);
|
2021-04-02 07:37:30 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
rcu_read_unlock();
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:28 +08:00
|
|
|
if (prev_root)
|
2021-04-02 07:37:32 +08:00
|
|
|
kvm_tdp_mmu_put_root(kvm, prev_root, shared);
|
2021-01-07 08:19:34 +08:00
|
|
|
|
|
|
|
return next_root;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: this iterator gets and puts references to the roots it iterates over.
|
|
|
|
* This makes it safe to release the MMU lock and yield within the loop, but
|
|
|
|
* if exiting the loop early, the caller must drop the reference to the most
|
|
|
|
* recent root. (Unless keeping a live reference is desirable.)
|
2021-04-02 07:37:32 +08:00
|
|
|
*
|
|
|
|
* If shared is set, this function is operating under the MMU lock in read
|
|
|
|
* mode. In the unlikely event that this thread must free a root, the lock
|
|
|
|
* will be temporarily dropped and reacquired in write mode.
|
2021-01-07 08:19:34 +08:00
|
|
|
*/
|
2021-04-02 07:37:32 +08:00
|
|
|
#define for_each_tdp_mmu_root_yield_safe(_kvm, _root, _as_id, _shared) \
|
|
|
|
for (_root = tdp_mmu_next_root(_kvm, NULL, _shared); \
|
|
|
|
_root; \
|
|
|
|
_root = tdp_mmu_next_root(_kvm, _root, _shared)) \
|
|
|
|
if (kvm_mmu_page_as_id(_root) != _as_id) { \
|
2021-03-26 10:19:45 +08:00
|
|
|
} else
|
2021-01-07 08:19:34 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
#define for_each_tdp_mmu_root(_kvm, _root, _as_id) \
|
|
|
|
list_for_each_entry_rcu(_root, &_kvm->arch.tdp_mmu_roots, link, \
|
|
|
|
lockdep_is_held_type(&kvm->mmu_lock, 0) || \
|
|
|
|
lockdep_is_held(&kvm->arch.tdp_mmu_pages_lock)) \
|
2021-03-26 10:19:45 +08:00
|
|
|
if (kvm_mmu_page_as_id(_root) != _as_id) { \
|
|
|
|
} else
|
2020-10-15 02:26:44 +08:00
|
|
|
|
|
|
|
static union kvm_mmu_page_role page_role_for_level(struct kvm_vcpu *vcpu,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
union kvm_mmu_page_role role;
|
|
|
|
|
|
|
|
role = vcpu->arch.mmu->mmu_role.base;
|
|
|
|
role.level = level;
|
|
|
|
role.direct = true;
|
|
|
|
role.gpte_is_8_bytes = true;
|
|
|
|
role.access = ACC_ALL;
|
|
|
|
|
|
|
|
return role;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct kvm_mmu_page *alloc_tdp_mmu_page(struct kvm_vcpu *vcpu, gfn_t gfn,
|
|
|
|
int level)
|
|
|
|
{
|
|
|
|
struct kvm_mmu_page *sp;
|
|
|
|
|
|
|
|
sp = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_page_header_cache);
|
|
|
|
sp->spt = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_shadow_page_cache);
|
|
|
|
set_page_private(virt_to_page(sp->spt), (unsigned long)sp);
|
|
|
|
|
|
|
|
sp->role.word = page_role_for_level(vcpu, level).word;
|
|
|
|
sp->gfn = gfn;
|
|
|
|
sp->tdp_mmu_page = true;
|
|
|
|
|
2020-10-28 01:59:43 +08:00
|
|
|
trace_kvm_mmu_get_page(sp, true);
|
|
|
|
|
2020-10-15 02:26:44 +08:00
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
2021-03-05 09:10:50 +08:00
|
|
|
hpa_t kvm_tdp_mmu_get_vcpu_root_hpa(struct kvm_vcpu *vcpu)
|
2020-10-15 02:26:44 +08:00
|
|
|
{
|
|
|
|
union kvm_mmu_page_role role;
|
|
|
|
struct kvm *kvm = vcpu->kvm;
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
|
2021-03-05 09:10:50 +08:00
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2020-10-15 02:26:44 +08:00
|
|
|
|
2021-03-05 09:10:50 +08:00
|
|
|
role = page_role_for_level(vcpu, vcpu->arch.mmu->shadow_root_level);
|
2020-10-15 02:26:44 +08:00
|
|
|
|
|
|
|
/* Check for an existing root before allocating a new one. */
|
2021-03-26 10:19:45 +08:00
|
|
|
for_each_tdp_mmu_root(kvm, root, kvm_mmu_role_as_id(role)) {
|
2021-04-02 07:37:30 +08:00
|
|
|
if (root->role.word == role.word &&
|
|
|
|
kvm_tdp_mmu_get_root(kvm, root))
|
2021-03-05 09:10:50 +08:00
|
|
|
goto out;
|
2020-10-15 02:26:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
root = alloc_tdp_mmu_page(vcpu, 0, vcpu->arch.mmu->shadow_root_level);
|
2021-04-02 07:37:29 +08:00
|
|
|
refcount_set(&root->tdp_mmu_root_count, 1);
|
2020-10-15 02:26:44 +08:00
|
|
|
|
2021-04-02 07:37:31 +08:00
|
|
|
spin_lock(&kvm->arch.tdp_mmu_pages_lock);
|
|
|
|
list_add_rcu(&root->link, &kvm->arch.tdp_mmu_roots);
|
|
|
|
spin_unlock(&kvm->arch.tdp_mmu_pages_lock);
|
2020-10-15 02:26:44 +08:00
|
|
|
|
2021-03-05 09:10:50 +08:00
|
|
|
out:
|
2020-10-15 02:26:44 +08:00
|
|
|
return __pa(root->spt);
|
2020-10-15 02:26:43 +08:00
|
|
|
}
|
2020-10-15 02:26:45 +08:00
|
|
|
|
|
|
|
static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn,
|
2021-02-03 02:57:26 +08:00
|
|
|
u64 old_spte, u64 new_spte, int level,
|
|
|
|
bool shared);
|
2020-10-15 02:26:45 +08:00
|
|
|
|
2020-10-15 02:26:53 +08:00
|
|
|
static void handle_changed_spte_acc_track(u64 old_spte, u64 new_spte, int level)
|
|
|
|
{
|
|
|
|
if (!is_shadow_present_pte(old_spte) || !is_last_spte(old_spte, level))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is_accessed_spte(old_spte) &&
|
2021-02-26 04:47:27 +08:00
|
|
|
(!is_shadow_present_pte(new_spte) || !is_accessed_spte(new_spte) ||
|
|
|
|
spte_to_pfn(old_spte) != spte_to_pfn(new_spte)))
|
2020-10-15 02:26:53 +08:00
|
|
|
kvm_set_pfn_accessed(spte_to_pfn(old_spte));
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
static void handle_changed_spte_dirty_log(struct kvm *kvm, int as_id, gfn_t gfn,
|
|
|
|
u64 old_spte, u64 new_spte, int level)
|
|
|
|
{
|
|
|
|
bool pfn_changed;
|
|
|
|
struct kvm_memory_slot *slot;
|
|
|
|
|
|
|
|
if (level > PG_LEVEL_4K)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pfn_changed = spte_to_pfn(old_spte) != spte_to_pfn(new_spte);
|
|
|
|
|
|
|
|
if ((!is_writable_pte(old_spte) || pfn_changed) &&
|
|
|
|
is_writable_pte(new_spte)) {
|
|
|
|
slot = __gfn_to_memslot(__kvm_memslots(kvm, as_id), gfn);
|
2020-10-01 09:22:22 +08:00
|
|
|
mark_page_dirty_in_slot(kvm, slot, gfn);
|
2020-10-15 02:26:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:25 +08:00
|
|
|
/**
|
|
|
|
* tdp_mmu_link_page - Add a new page to the list of pages used by the TDP MMU
|
|
|
|
*
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @sp: the new page
|
2021-02-03 02:57:26 +08:00
|
|
|
* @shared: This operation may not be running under the exclusive use of
|
|
|
|
* the MMU lock and the operation must synchronize with other
|
|
|
|
* threads that might be adding or removing pages.
|
2021-02-03 02:57:25 +08:00
|
|
|
* @account_nx: This page replaces a NX large page and should be marked for
|
|
|
|
* eventual reclaim.
|
|
|
|
*/
|
|
|
|
static void tdp_mmu_link_page(struct kvm *kvm, struct kvm_mmu_page *sp,
|
2021-02-03 02:57:26 +08:00
|
|
|
bool shared, bool account_nx)
|
2021-02-03 02:57:25 +08:00
|
|
|
{
|
2021-02-03 02:57:26 +08:00
|
|
|
if (shared)
|
|
|
|
spin_lock(&kvm->arch.tdp_mmu_pages_lock);
|
|
|
|
else
|
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2021-02-03 02:57:25 +08:00
|
|
|
|
|
|
|
list_add(&sp->link, &kvm->arch.tdp_mmu_pages);
|
|
|
|
if (account_nx)
|
|
|
|
account_huge_nx_page(kvm, sp);
|
2021-02-03 02:57:26 +08:00
|
|
|
|
|
|
|
if (shared)
|
|
|
|
spin_unlock(&kvm->arch.tdp_mmu_pages_lock);
|
2021-02-03 02:57:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tdp_mmu_unlink_page - Remove page from the list of pages used by the TDP MMU
|
|
|
|
*
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @sp: the page to be removed
|
2021-02-03 02:57:26 +08:00
|
|
|
* @shared: This operation may not be running under the exclusive use of
|
|
|
|
* the MMU lock and the operation must synchronize with other
|
|
|
|
* threads that might be adding or removing pages.
|
2021-02-03 02:57:25 +08:00
|
|
|
*/
|
2021-02-03 02:57:26 +08:00
|
|
|
static void tdp_mmu_unlink_page(struct kvm *kvm, struct kvm_mmu_page *sp,
|
|
|
|
bool shared)
|
2021-02-03 02:57:25 +08:00
|
|
|
{
|
2021-02-03 02:57:26 +08:00
|
|
|
if (shared)
|
|
|
|
spin_lock(&kvm->arch.tdp_mmu_pages_lock);
|
|
|
|
else
|
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2021-02-03 02:57:25 +08:00
|
|
|
|
|
|
|
list_del(&sp->link);
|
|
|
|
if (sp->lpage_disallowed)
|
|
|
|
unaccount_huge_nx_page(kvm, sp);
|
2021-02-03 02:57:26 +08:00
|
|
|
|
|
|
|
if (shared)
|
|
|
|
spin_unlock(&kvm->arch.tdp_mmu_pages_lock);
|
2021-02-03 02:57:25 +08:00
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:11 +08:00
|
|
|
/**
|
|
|
|
* handle_removed_tdp_mmu_page - handle a pt removed from the TDP structure
|
|
|
|
*
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @pt: the page removed from the paging structure
|
2021-02-03 02:57:26 +08:00
|
|
|
* @shared: This operation may not be running under the exclusive use
|
|
|
|
* of the MMU lock and the operation must synchronize with other
|
|
|
|
* threads that might be modifying SPTEs.
|
2021-02-03 02:57:11 +08:00
|
|
|
*
|
|
|
|
* Given a page table that has been removed from the TDP paging structure,
|
|
|
|
* iterates through the page table to clear SPTEs and free child page tables.
|
2021-03-16 07:38:00 +08:00
|
|
|
*
|
|
|
|
* Note that pt is passed in as a tdp_ptep_t, but it does not need RCU
|
|
|
|
* protection. Since this thread removed it from the paging structure,
|
|
|
|
* this thread will be responsible for ensuring the page is freed. Hence the
|
|
|
|
* early rcu_dereferences in the function.
|
2021-02-03 02:57:11 +08:00
|
|
|
*/
|
2021-03-16 07:38:00 +08:00
|
|
|
static void handle_removed_tdp_mmu_page(struct kvm *kvm, tdp_ptep_t pt,
|
2021-02-03 02:57:26 +08:00
|
|
|
bool shared)
|
2021-02-03 02:57:11 +08:00
|
|
|
{
|
2021-03-16 07:38:00 +08:00
|
|
|
struct kvm_mmu_page *sp = sptep_to_sp(rcu_dereference(pt));
|
2021-02-03 02:57:11 +08:00
|
|
|
int level = sp->role.level;
|
2021-02-03 02:57:28 +08:00
|
|
|
gfn_t base_gfn = sp->gfn;
|
2021-02-03 02:57:11 +08:00
|
|
|
u64 old_child_spte;
|
2021-02-03 02:57:26 +08:00
|
|
|
u64 *sptep;
|
2021-02-03 02:57:28 +08:00
|
|
|
gfn_t gfn;
|
2021-02-03 02:57:11 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
trace_kvm_mmu_prepare_zap_page(sp);
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
tdp_mmu_unlink_page(kvm, sp, shared);
|
2021-02-03 02:57:11 +08:00
|
|
|
|
|
|
|
for (i = 0; i < PT64_ENT_PER_PAGE; i++) {
|
2021-03-16 07:38:00 +08:00
|
|
|
sptep = rcu_dereference(pt) + i;
|
2021-02-03 02:57:28 +08:00
|
|
|
gfn = base_gfn + (i * KVM_PAGES_PER_HPAGE(level - 1));
|
2021-02-03 02:57:26 +08:00
|
|
|
|
|
|
|
if (shared) {
|
2021-02-03 02:57:28 +08:00
|
|
|
/*
|
|
|
|
* Set the SPTE to a nonpresent value that other
|
|
|
|
* threads will not overwrite. If the SPTE was
|
|
|
|
* already marked as removed then another thread
|
|
|
|
* handling a page fault could overwrite it, so
|
|
|
|
* set the SPTE until it is set from some other
|
|
|
|
* value to the removed SPTE value.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
old_child_spte = xchg(sptep, REMOVED_SPTE);
|
|
|
|
if (!is_removed_spte(old_child_spte))
|
|
|
|
break;
|
|
|
|
cpu_relax();
|
|
|
|
}
|
2021-02-03 02:57:26 +08:00
|
|
|
} else {
|
2021-03-10 08:30:29 +08:00
|
|
|
/*
|
|
|
|
* If the SPTE is not MMU-present, there is no backing
|
|
|
|
* page associated with the SPTE and so no side effects
|
|
|
|
* that need to be recorded, and exclusive ownership of
|
|
|
|
* mmu_lock ensures the SPTE can't be made present.
|
|
|
|
* Note, zapping MMIO SPTEs is also unnecessary as they
|
|
|
|
* are guarded by the memslots generation, not by being
|
|
|
|
* unreachable.
|
|
|
|
*/
|
2021-02-03 02:57:26 +08:00
|
|
|
old_child_spte = READ_ONCE(*sptep);
|
2021-03-10 08:30:29 +08:00
|
|
|
if (!is_shadow_present_pte(old_child_spte))
|
|
|
|
continue;
|
2021-02-03 02:57:28 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Marking the SPTE as a removed SPTE is not
|
|
|
|
* strictly necessary here as the MMU lock will
|
|
|
|
* stop other threads from concurrently modifying
|
|
|
|
* this SPTE. Using the removed SPTE value keeps
|
|
|
|
* the two branches consistent and simplifies
|
|
|
|
* the function.
|
|
|
|
*/
|
|
|
|
WRITE_ONCE(*sptep, REMOVED_SPTE);
|
2021-02-03 02:57:26 +08:00
|
|
|
}
|
2021-02-03 02:57:28 +08:00
|
|
|
handle_changed_spte(kvm, kvm_mmu_page_as_id(sp), gfn,
|
|
|
|
old_child_spte, REMOVED_SPTE, level - 1,
|
|
|
|
shared);
|
2021-02-03 02:57:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kvm_flush_remote_tlbs_with_address(kvm, gfn,
|
|
|
|
KVM_PAGES_PER_HPAGE(level));
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
call_rcu(&sp->rcu_head, tdp_mmu_free_sp_rcu_callback);
|
2021-02-03 02:57:11 +08:00
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:45 +08:00
|
|
|
/**
|
|
|
|
* handle_changed_spte - handle bookkeeping associated with an SPTE change
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @as_id: the address space of the paging structure the SPTE was a part of
|
|
|
|
* @gfn: the base GFN that was mapped by the SPTE
|
|
|
|
* @old_spte: The value of the SPTE before the change
|
|
|
|
* @new_spte: The value of the SPTE after the change
|
|
|
|
* @level: the level of the PT the SPTE is part of in the paging structure
|
2021-02-03 02:57:26 +08:00
|
|
|
* @shared: This operation may not be running under the exclusive use of
|
|
|
|
* the MMU lock and the operation must synchronize with other
|
|
|
|
* threads that might be modifying SPTEs.
|
2020-10-15 02:26:45 +08:00
|
|
|
*
|
|
|
|
* Handle bookkeeping that might result from the modification of a SPTE.
|
|
|
|
* This function must be called for all TDP SPTE modifications.
|
|
|
|
*/
|
|
|
|
static void __handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn,
|
2021-02-03 02:57:26 +08:00
|
|
|
u64 old_spte, u64 new_spte, int level,
|
|
|
|
bool shared)
|
2020-10-15 02:26:45 +08:00
|
|
|
{
|
|
|
|
bool was_present = is_shadow_present_pte(old_spte);
|
|
|
|
bool is_present = is_shadow_present_pte(new_spte);
|
|
|
|
bool was_leaf = was_present && is_last_spte(old_spte, level);
|
|
|
|
bool is_leaf = is_present && is_last_spte(new_spte, level);
|
|
|
|
bool pfn_changed = spte_to_pfn(old_spte) != spte_to_pfn(new_spte);
|
|
|
|
|
|
|
|
WARN_ON(level > PT64_ROOT_MAX_LEVEL);
|
|
|
|
WARN_ON(level < PG_LEVEL_4K);
|
2020-10-24 11:11:50 +08:00
|
|
|
WARN_ON(gfn & (KVM_PAGES_PER_HPAGE(level) - 1));
|
2020-10-15 02:26:45 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this warning were to trigger it would indicate that there was a
|
|
|
|
* missing MMU notifier or a race with some notifier handler.
|
|
|
|
* A present, leaf SPTE should never be directly replaced with another
|
2021-03-18 22:28:01 +08:00
|
|
|
* present leaf SPTE pointing to a different PFN. A notifier handler
|
2020-10-15 02:26:45 +08:00
|
|
|
* should be zapping the SPTE before the main MM's page table is
|
|
|
|
* changed, or the SPTE should be zeroed, and the TLBs flushed by the
|
|
|
|
* thread before replacement.
|
|
|
|
*/
|
|
|
|
if (was_leaf && is_leaf && pfn_changed) {
|
|
|
|
pr_err("Invalid SPTE change: cannot replace a present leaf\n"
|
|
|
|
"SPTE with another present leaf SPTE mapping a\n"
|
|
|
|
"different PFN!\n"
|
|
|
|
"as_id: %d gfn: %llx old_spte: %llx new_spte: %llx level: %d",
|
|
|
|
as_id, gfn, old_spte, new_spte, level);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Crash the host to prevent error propagation and guest data
|
2021-03-18 22:28:01 +08:00
|
|
|
* corruption.
|
2020-10-15 02:26:45 +08:00
|
|
|
*/
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_spte == new_spte)
|
|
|
|
return;
|
|
|
|
|
2020-10-28 01:59:44 +08:00
|
|
|
trace_kvm_tdp_mmu_spte_changed(as_id, gfn, level, old_spte, new_spte);
|
|
|
|
|
2020-10-15 02:26:45 +08:00
|
|
|
/*
|
|
|
|
* The only times a SPTE should be changed from a non-present to
|
|
|
|
* non-present state is when an MMIO entry is installed/modified/
|
|
|
|
* removed. In that case, there is nothing to do here.
|
|
|
|
*/
|
|
|
|
if (!was_present && !is_present) {
|
|
|
|
/*
|
2021-02-03 02:57:27 +08:00
|
|
|
* If this change does not involve a MMIO SPTE or removed SPTE,
|
|
|
|
* it is unexpected. Log the change, though it should not
|
|
|
|
* impact the guest since both the former and current SPTEs
|
|
|
|
* are nonpresent.
|
2020-10-15 02:26:45 +08:00
|
|
|
*/
|
2021-02-03 02:57:27 +08:00
|
|
|
if (WARN_ON(!is_mmio_spte(old_spte) &&
|
|
|
|
!is_mmio_spte(new_spte) &&
|
|
|
|
!is_removed_spte(new_spte)))
|
2020-10-15 02:26:45 +08:00
|
|
|
pr_err("Unexpected SPTE change! Nonpresent SPTEs\n"
|
|
|
|
"should not be replaced with another,\n"
|
|
|
|
"different nonpresent SPTE, unless one or both\n"
|
2021-02-03 02:57:27 +08:00
|
|
|
"are MMIO SPTEs, or the new SPTE is\n"
|
|
|
|
"a temporary removed SPTE.\n"
|
2020-10-15 02:26:45 +08:00
|
|
|
"as_id: %d gfn: %llx old_spte: %llx new_spte: %llx level: %d",
|
|
|
|
as_id, gfn, old_spte, new_spte, level);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (was_leaf && is_dirty_spte(old_spte) &&
|
2021-02-26 04:47:27 +08:00
|
|
|
(!is_present || !is_dirty_spte(new_spte) || pfn_changed))
|
2020-10-15 02:26:45 +08:00
|
|
|
kvm_set_pfn_dirty(spte_to_pfn(old_spte));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recursively handle child PTs if the change removed a subtree from
|
|
|
|
* the paging structure.
|
|
|
|
*/
|
2021-02-03 02:57:11 +08:00
|
|
|
if (was_present && !was_leaf && (pfn_changed || !is_present))
|
|
|
|
handle_removed_tdp_mmu_page(kvm,
|
2021-02-03 02:57:26 +08:00
|
|
|
spte_to_child_pt(old_spte, level), shared);
|
2020-10-15 02:26:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn,
|
2021-02-03 02:57:26 +08:00
|
|
|
u64 old_spte, u64 new_spte, int level,
|
|
|
|
bool shared)
|
2020-10-15 02:26:45 +08:00
|
|
|
{
|
2021-02-03 02:57:26 +08:00
|
|
|
__handle_changed_spte(kvm, as_id, gfn, old_spte, new_spte, level,
|
|
|
|
shared);
|
2020-10-15 02:26:53 +08:00
|
|
|
handle_changed_spte_acc_track(old_spte, new_spte, level);
|
2020-10-15 02:26:55 +08:00
|
|
|
handle_changed_spte_dirty_log(kvm, as_id, gfn, old_spte,
|
|
|
|
new_spte, level);
|
2020-10-15 02:26:45 +08:00
|
|
|
}
|
2020-10-15 02:26:47 +08:00
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
/*
|
2021-04-02 07:37:34 +08:00
|
|
|
* tdp_mmu_set_spte_atomic_no_dirty_log - Set a TDP MMU SPTE atomically
|
|
|
|
* and handle the associated bookkeeping, but do not mark the page dirty
|
|
|
|
* in KVM's dirty bitmaps.
|
2021-02-03 02:57:26 +08:00
|
|
|
*
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @iter: a tdp_iter instance currently on the SPTE that should be set
|
|
|
|
* @new_spte: The value the SPTE should be set to
|
|
|
|
* Returns: true if the SPTE was set, false if it was not. If false is returned,
|
|
|
|
* this function will have no side-effects.
|
|
|
|
*/
|
2021-04-02 07:37:34 +08:00
|
|
|
static inline bool tdp_mmu_set_spte_atomic_no_dirty_log(struct kvm *kvm,
|
|
|
|
struct tdp_iter *iter,
|
|
|
|
u64 new_spte)
|
2021-02-03 02:57:26 +08:00
|
|
|
{
|
|
|
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
|
|
|
|
2021-02-03 02:57:27 +08:00
|
|
|
/*
|
|
|
|
* Do not change removed SPTEs. Only the thread that froze the SPTE
|
|
|
|
* may modify it.
|
|
|
|
*/
|
2021-02-26 04:47:47 +08:00
|
|
|
if (is_removed_spte(iter->old_spte))
|
2021-02-03 02:57:27 +08:00
|
|
|
return false;
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
if (cmpxchg64(rcu_dereference(iter->sptep), iter->old_spte,
|
|
|
|
new_spte) != iter->old_spte)
|
|
|
|
return false;
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
__handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte,
|
|
|
|
new_spte, iter->level, true);
|
|
|
|
handle_changed_spte_acc_track(iter->old_spte, new_spte, iter->level);
|
2021-02-03 02:57:26 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
static inline bool tdp_mmu_set_spte_atomic(struct kvm *kvm,
|
|
|
|
struct tdp_iter *iter,
|
|
|
|
u64 new_spte)
|
|
|
|
{
|
|
|
|
if (!tdp_mmu_set_spte_atomic_no_dirty_log(kvm, iter, new_spte))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
handle_changed_spte_dirty_log(kvm, iter->as_id, iter->gfn,
|
|
|
|
iter->old_spte, new_spte, iter->level);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:27 +08:00
|
|
|
static inline bool tdp_mmu_zap_spte_atomic(struct kvm *kvm,
|
|
|
|
struct tdp_iter *iter)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Freeze the SPTE by setting it to a special,
|
|
|
|
* non-present value. This will stop other threads from
|
|
|
|
* immediately installing a present entry in its place
|
|
|
|
* before the TLBs are flushed.
|
|
|
|
*/
|
|
|
|
if (!tdp_mmu_set_spte_atomic(kvm, iter, REMOVED_SPTE))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
kvm_flush_remote_tlbs_with_address(kvm, iter->gfn,
|
|
|
|
KVM_PAGES_PER_HPAGE(iter->level));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No other thread can overwrite the removed SPTE as they
|
|
|
|
* must either wait on the MMU lock or use
|
2021-03-18 22:28:01 +08:00
|
|
|
* tdp_mmu_set_spte_atomic which will not overwrite the
|
2021-02-03 02:57:27 +08:00
|
|
|
* special removed SPTE value. No bookkeeping is needed
|
|
|
|
* here since the SPTE is going from non-present
|
|
|
|
* to non-present.
|
|
|
|
*/
|
2021-03-16 07:38:01 +08:00
|
|
|
WRITE_ONCE(*rcu_dereference(iter->sptep), 0);
|
2021-02-03 02:57:27 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
|
2021-02-03 02:57:08 +08:00
|
|
|
/*
|
|
|
|
* __tdp_mmu_set_spte - Set a TDP MMU SPTE and handle the associated bookkeeping
|
|
|
|
* @kvm: kvm instance
|
|
|
|
* @iter: a tdp_iter instance currently on the SPTE that should be set
|
|
|
|
* @new_spte: The value the SPTE should be set to
|
|
|
|
* @record_acc_track: Notify the MM subsystem of changes to the accessed state
|
|
|
|
* of the page. Should be set unless handling an MMU
|
|
|
|
* notifier for access tracking. Leaving record_acc_track
|
|
|
|
* unset in that case prevents page accesses from being
|
|
|
|
* double counted.
|
|
|
|
* @record_dirty_log: Record the page as dirty in the dirty bitmap if
|
|
|
|
* appropriate for the change being made. Should be set
|
|
|
|
* unless performing certain dirty logging operations.
|
|
|
|
* Leaving record_dirty_log unset in that case prevents page
|
|
|
|
* writes from being double counted.
|
|
|
|
*/
|
2020-10-15 02:26:53 +08:00
|
|
|
static inline void __tdp_mmu_set_spte(struct kvm *kvm, struct tdp_iter *iter,
|
2020-10-15 02:26:55 +08:00
|
|
|
u64 new_spte, bool record_acc_track,
|
|
|
|
bool record_dirty_log)
|
2020-10-15 02:26:47 +08:00
|
|
|
{
|
2021-02-03 02:57:24 +08:00
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2021-02-03 02:57:09 +08:00
|
|
|
|
2021-02-03 02:57:27 +08:00
|
|
|
/*
|
|
|
|
* No thread should be using this function to set SPTEs to the
|
|
|
|
* temporary removed SPTE value.
|
|
|
|
* If operating under the MMU lock in read mode, tdp_mmu_set_spte_atomic
|
|
|
|
* should be used. If operating under the MMU lock in write mode, the
|
|
|
|
* use of the removed SPTE should not be necessary.
|
|
|
|
*/
|
2021-02-26 04:47:47 +08:00
|
|
|
WARN_ON(is_removed_spte(iter->old_spte));
|
2021-02-03 02:57:27 +08:00
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
WRITE_ONCE(*rcu_dereference(iter->sptep), new_spte);
|
2020-10-15 02:26:53 +08:00
|
|
|
|
2021-03-16 07:38:03 +08:00
|
|
|
__handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte,
|
|
|
|
new_spte, iter->level, false);
|
2020-10-15 02:26:53 +08:00
|
|
|
if (record_acc_track)
|
|
|
|
handle_changed_spte_acc_track(iter->old_spte, new_spte,
|
|
|
|
iter->level);
|
2020-10-15 02:26:55 +08:00
|
|
|
if (record_dirty_log)
|
2021-03-16 07:38:03 +08:00
|
|
|
handle_changed_spte_dirty_log(kvm, iter->as_id, iter->gfn,
|
2020-10-15 02:26:55 +08:00
|
|
|
iter->old_spte, new_spte,
|
|
|
|
iter->level);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void tdp_mmu_set_spte(struct kvm *kvm, struct tdp_iter *iter,
|
|
|
|
u64 new_spte)
|
|
|
|
{
|
2020-10-15 02:26:55 +08:00
|
|
|
__tdp_mmu_set_spte(kvm, iter, new_spte, true, true);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
2020-10-15 02:26:47 +08:00
|
|
|
|
2020-10-15 02:26:53 +08:00
|
|
|
static inline void tdp_mmu_set_spte_no_acc_track(struct kvm *kvm,
|
|
|
|
struct tdp_iter *iter,
|
|
|
|
u64 new_spte)
|
|
|
|
{
|
2020-10-15 02:26:55 +08:00
|
|
|
__tdp_mmu_set_spte(kvm, iter, new_spte, false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void tdp_mmu_set_spte_no_dirty_log(struct kvm *kvm,
|
|
|
|
struct tdp_iter *iter,
|
|
|
|
u64 new_spte)
|
|
|
|
{
|
|
|
|
__tdp_mmu_set_spte(kvm, iter, new_spte, true, false);
|
2020-10-15 02:26:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define tdp_root_for_each_pte(_iter, _root, _start, _end) \
|
|
|
|
for_each_tdp_pte(_iter, _root->spt, _root->role.level, _start, _end)
|
|
|
|
|
2020-10-15 02:26:53 +08:00
|
|
|
#define tdp_root_for_each_leaf_pte(_iter, _root, _start, _end) \
|
|
|
|
tdp_root_for_each_pte(_iter, _root, _start, _end) \
|
|
|
|
if (!is_shadow_present_pte(_iter.old_spte) || \
|
|
|
|
!is_last_spte(_iter.old_spte, _iter.level)) \
|
|
|
|
continue; \
|
|
|
|
else
|
|
|
|
|
2020-10-15 02:26:50 +08:00
|
|
|
#define tdp_mmu_for_each_pte(_iter, _mmu, _start, _end) \
|
|
|
|
for_each_tdp_pte(_iter, __va(_mmu->root_hpa), \
|
|
|
|
_mmu->shadow_root_level, _start, _end)
|
|
|
|
|
2021-02-03 02:57:07 +08:00
|
|
|
/*
|
|
|
|
* Yield if the MMU lock is contended or this thread needs to return control
|
|
|
|
* to the scheduler.
|
|
|
|
*
|
2021-02-03 02:57:17 +08:00
|
|
|
* If this function should yield and flush is set, it will perform a remote
|
|
|
|
* TLB flush before yielding.
|
|
|
|
*
|
2021-02-03 02:57:07 +08:00
|
|
|
* If this function yields, it will also reset the tdp_iter's walk over the
|
2021-02-03 02:57:19 +08:00
|
|
|
* paging structure and the calling function should skip to the next
|
|
|
|
* iteration to allow the iterator to continue its traversal from the
|
|
|
|
* paging structure root.
|
2021-02-03 02:57:07 +08:00
|
|
|
*
|
|
|
|
* Return true if this function yielded and the iterator's traversal was reset.
|
|
|
|
* Return false if a yield was not needed.
|
|
|
|
*/
|
2021-02-03 02:57:17 +08:00
|
|
|
static inline bool tdp_mmu_iter_cond_resched(struct kvm *kvm,
|
2021-04-02 07:37:32 +08:00
|
|
|
struct tdp_iter *iter, bool flush,
|
|
|
|
bool shared)
|
2020-10-15 02:26:55 +08:00
|
|
|
{
|
2021-02-03 02:57:19 +08:00
|
|
|
/* Ensure forward progress has been made before yielding. */
|
|
|
|
if (iter->next_last_level_gfn == iter->yielded_gfn)
|
|
|
|
return false;
|
|
|
|
|
2021-02-03 02:57:24 +08:00
|
|
|
if (need_resched() || rwlock_needbreak(&kvm->mmu_lock)) {
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2021-02-03 02:57:17 +08:00
|
|
|
if (flush)
|
|
|
|
kvm_flush_remote_tlbs(kvm);
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
if (shared)
|
|
|
|
cond_resched_rwlock_read(&kvm->mmu_lock);
|
|
|
|
else
|
|
|
|
cond_resched_rwlock_write(&kvm->mmu_lock);
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
2021-02-03 02:57:19 +08:00
|
|
|
|
|
|
|
WARN_ON(iter->gfn > iter->next_last_level_gfn);
|
|
|
|
|
2021-03-16 07:38:02 +08:00
|
|
|
tdp_iter_restart(iter);
|
2021-02-03 02:57:19 +08:00
|
|
|
|
2021-02-03 02:57:07 +08:00
|
|
|
return true;
|
2020-10-15 02:26:55 +08:00
|
|
|
}
|
2021-02-03 02:57:07 +08:00
|
|
|
|
|
|
|
return false;
|
2020-10-15 02:26:55 +08:00
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:47 +08:00
|
|
|
/*
|
|
|
|
* Tears down the mappings for the range of gfns, [start, end), and frees the
|
|
|
|
* non-root pages mapping GFNs strictly within that range. Returns true if
|
|
|
|
* SPTEs have been cleared and a TLB flush is needed before releasing the
|
|
|
|
* MMU lock.
|
2021-04-02 07:37:32 +08:00
|
|
|
*
|
2020-10-15 02:26:52 +08:00
|
|
|
* If can_yield is true, will release the MMU lock and reschedule if the
|
|
|
|
* scheduler needs the CPU or there is contention on the MMU lock. If this
|
|
|
|
* function cannot yield, it will not release the MMU lock or reschedule and
|
|
|
|
* the caller must ensure it does not supply too large a GFN range, or the
|
2021-04-02 07:37:32 +08:00
|
|
|
* operation can cause a soft lockup.
|
|
|
|
*
|
|
|
|
* If shared is true, this thread holds the MMU lock in read mode and must
|
|
|
|
* account for the possibility that other threads are modifying the paging
|
|
|
|
* structures concurrently. If shared is false, this thread should hold the
|
|
|
|
* MMU lock in write mode.
|
2020-10-15 02:26:47 +08:00
|
|
|
*/
|
|
|
|
static bool zap_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
|
2021-04-02 07:37:32 +08:00
|
|
|
gfn_t start, gfn_t end, bool can_yield, bool flush,
|
|
|
|
bool shared)
|
2020-10-15 02:26:47 +08:00
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
kvm_lockdep_assert_mmu_lock_held(kvm, shared);
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:47 +08:00
|
|
|
tdp_root_for_each_pte(iter, root, start, end) {
|
2021-04-02 07:37:32 +08:00
|
|
|
retry:
|
2021-02-03 02:57:20 +08:00
|
|
|
if (can_yield &&
|
2021-04-02 07:37:32 +08:00
|
|
|
tdp_mmu_iter_cond_resched(kvm, &iter, flush, shared)) {
|
2021-03-26 04:01:17 +08:00
|
|
|
flush = false;
|
2021-02-03 02:57:20 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:47 +08:00
|
|
|
if (!is_shadow_present_pte(iter.old_spte))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a non-last-level SPTE that covers a larger range
|
|
|
|
* than should be zapped, continue, and zap the mappings at a
|
|
|
|
* lower level.
|
|
|
|
*/
|
|
|
|
if ((iter.gfn < start ||
|
|
|
|
iter.gfn + KVM_PAGES_PER_HPAGE(iter.level) > end) &&
|
|
|
|
!is_last_spte(iter.old_spte, iter.level))
|
|
|
|
continue;
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
if (!shared) {
|
|
|
|
tdp_mmu_set_spte(kvm, &iter, 0);
|
|
|
|
flush = true;
|
|
|
|
} else if (!tdp_mmu_zap_spte_atomic(kvm, &iter)) {
|
|
|
|
/*
|
|
|
|
* The iter must explicitly re-read the SPTE because
|
|
|
|
* the atomic cmpxchg failed.
|
|
|
|
*/
|
|
|
|
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
|
|
|
goto retry;
|
|
|
|
}
|
2020-10-15 02:26:47 +08:00
|
|
|
}
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2021-03-26 04:01:17 +08:00
|
|
|
return flush;
|
2020-10-15 02:26:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tears down the mappings for the range of gfns, [start, end), and frees the
|
|
|
|
* non-root pages mapping GFNs strictly within that range. Returns true if
|
|
|
|
* SPTEs have been cleared and a TLB flush is needed before releasing the
|
|
|
|
* MMU lock.
|
2021-04-02 07:37:32 +08:00
|
|
|
*
|
|
|
|
* If shared is true, this thread holds the MMU lock in read mode and must
|
|
|
|
* account for the possibility that other threads are modifying the paging
|
|
|
|
* structures concurrently. If shared is false, this thread should hold the
|
|
|
|
* MMU in write mode.
|
2020-10-15 02:26:47 +08:00
|
|
|
*/
|
2021-03-26 10:19:44 +08:00
|
|
|
bool __kvm_tdp_mmu_zap_gfn_range(struct kvm *kvm, int as_id, gfn_t start,
|
2021-04-02 07:37:32 +08:00
|
|
|
gfn_t end, bool can_yield, bool flush,
|
|
|
|
bool shared)
|
2020-10-15 02:26:47 +08:00
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
|
2021-04-02 07:37:32 +08:00
|
|
|
for_each_tdp_mmu_root_yield_safe(kvm, root, as_id, shared)
|
|
|
|
flush = zap_gfn_range(kvm, root, start, end, can_yield, flush,
|
|
|
|
shared);
|
2020-10-15 02:26:47 +08:00
|
|
|
|
|
|
|
return flush;
|
|
|
|
}
|
|
|
|
|
|
|
|
void kvm_tdp_mmu_zap_all(struct kvm *kvm)
|
|
|
|
{
|
2020-12-04 07:11:20 +08:00
|
|
|
gfn_t max_gfn = 1ULL << (shadow_phys_bits - PAGE_SHIFT);
|
2021-03-26 10:19:44 +08:00
|
|
|
bool flush = false;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
|
2021-04-02 07:37:32 +08:00
|
|
|
flush = kvm_tdp_mmu_zap_gfn_range(kvm, i, 0, max_gfn,
|
|
|
|
flush, false);
|
2020-10-15 02:26:47 +08:00
|
|
|
|
|
|
|
if (flush)
|
|
|
|
kvm_flush_remote_tlbs(kvm);
|
|
|
|
}
|
2020-10-15 02:26:50 +08:00
|
|
|
|
2021-04-02 07:37:36 +08:00
|
|
|
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();
|
2020-10-15 02:26:47 +08:00
|
|
|
|
|
|
|
if (flush)
|
|
|
|
kvm_flush_remote_tlbs(kvm);
|
|
|
|
}
|
2020-10-15 02:26:50 +08:00
|
|
|
|
2021-04-02 07:37:35 +08:00
|
|
|
/*
|
|
|
|
* Mark each TDP MMU root as invalid so that other threads
|
|
|
|
* will drop their references and allow the root count to
|
|
|
|
* go to 0.
|
|
|
|
*
|
2021-04-02 07:37:36 +08:00
|
|
|
* 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.
|
|
|
|
*
|
2021-04-02 07:37:35 +08:00
|
|
|
* This has essentially the same effect for the TDP MMU
|
|
|
|
* as updating mmu_valid_gen does for the shadow MMU.
|
|
|
|
*/
|
|
|
|
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)
|
2021-04-02 07:37:36 +08:00
|
|
|
if (refcount_inc_not_zero(&root->tdp_mmu_root_count))
|
|
|
|
root->role.invalid = true;
|
2021-04-02 07:37:35 +08:00
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:50 +08:00
|
|
|
/*
|
|
|
|
* Installs a last-level SPTE to handle a TDP page fault.
|
|
|
|
* (NPT/EPT violation/misconfiguration)
|
|
|
|
*/
|
|
|
|
static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu, int write,
|
|
|
|
int map_writable,
|
|
|
|
struct tdp_iter *iter,
|
|
|
|
kvm_pfn_t pfn, bool prefault)
|
|
|
|
{
|
|
|
|
u64 new_spte;
|
|
|
|
int ret = 0;
|
|
|
|
int make_spte_ret = 0;
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
if (unlikely(is_noslot_pfn(pfn)))
|
2020-10-15 02:26:50 +08:00
|
|
|
new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL);
|
2021-02-03 02:57:26 +08:00
|
|
|
else
|
2020-10-15 02:26:50 +08:00
|
|
|
make_spte_ret = make_spte(vcpu, ACC_ALL, iter->level, iter->gfn,
|
|
|
|
pfn, iter->old_spte, prefault, true,
|
|
|
|
map_writable, !shadow_accessed_mask,
|
|
|
|
&new_spte);
|
|
|
|
|
|
|
|
if (new_spte == iter->old_spte)
|
|
|
|
ret = RET_PF_SPURIOUS;
|
2021-02-03 02:57:26 +08:00
|
|
|
else if (!tdp_mmu_set_spte_atomic(vcpu->kvm, iter, new_spte))
|
|
|
|
return RET_PF_RETRY;
|
2020-10-15 02:26:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the page fault was caused by a write but the page is write
|
|
|
|
* protected, emulation is needed. If the emulation was skipped,
|
|
|
|
* the vCPU would have the same fault again.
|
|
|
|
*/
|
|
|
|
if (make_spte_ret & SET_SPTE_WRITE_PROTECTED_PT) {
|
|
|
|
if (write)
|
|
|
|
ret = RET_PF_EMULATE;
|
|
|
|
kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If a MMIO SPTE is installed, the MMIO will need to be emulated. */
|
2021-02-03 02:57:26 +08:00
|
|
|
if (unlikely(is_mmio_spte(new_spte))) {
|
|
|
|
trace_mark_mmio_spte(rcu_dereference(iter->sptep), iter->gfn,
|
|
|
|
new_spte);
|
2020-10-15 02:26:50 +08:00
|
|
|
ret = RET_PF_EMULATE;
|
2021-02-26 04:47:33 +08:00
|
|
|
} else {
|
2021-02-03 02:57:26 +08:00
|
|
|
trace_kvm_mmu_set_spte(iter->level, iter->gfn,
|
|
|
|
rcu_dereference(iter->sptep));
|
2021-02-26 04:47:33 +08:00
|
|
|
}
|
2020-10-15 02:26:50 +08:00
|
|
|
|
|
|
|
if (!prefault)
|
|
|
|
vcpu->stat.pf_fixed++;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle a TDP page fault (NPT/EPT violation/misconfiguration) by installing
|
|
|
|
* page tables and SPTEs to translate the faulting guest physical address.
|
|
|
|
*/
|
|
|
|
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,
|
|
|
|
bool prefault)
|
|
|
|
{
|
|
|
|
bool nx_huge_page_workaround_enabled = is_nx_huge_page_enabled();
|
|
|
|
bool write = error_code & PFERR_WRITE_MASK;
|
|
|
|
bool exec = error_code & PFERR_FETCH_MASK;
|
|
|
|
bool huge_page_disallowed = exec && nx_huge_page_workaround_enabled;
|
|
|
|
struct kvm_mmu *mmu = vcpu->arch.mmu;
|
|
|
|
struct tdp_iter iter;
|
2020-10-15 02:26:51 +08:00
|
|
|
struct kvm_mmu_page *sp;
|
2020-10-15 02:26:50 +08:00
|
|
|
u64 *child_pt;
|
|
|
|
u64 new_spte;
|
|
|
|
int ret;
|
|
|
|
gfn_t gfn = gpa >> PAGE_SHIFT;
|
|
|
|
int level;
|
|
|
|
int req_level;
|
|
|
|
|
|
|
|
if (WARN_ON(!VALID_PAGE(vcpu->arch.mmu->root_hpa)))
|
|
|
|
return RET_PF_RETRY;
|
|
|
|
if (WARN_ON(!is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa)))
|
|
|
|
return RET_PF_RETRY;
|
|
|
|
|
|
|
|
level = kvm_mmu_hugepage_adjust(vcpu, gfn, max_level, &pfn,
|
|
|
|
huge_page_disallowed, &req_level);
|
|
|
|
|
|
|
|
trace_kvm_mmu_spte_requested(gpa, level, pfn);
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:50 +08:00
|
|
|
tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
|
|
|
|
if (nx_huge_page_workaround_enabled)
|
|
|
|
disallowed_hugepage_adjust(iter.old_spte, gfn,
|
|
|
|
iter.level, &pfn, &level);
|
|
|
|
|
|
|
|
if (iter.level == level)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is an SPTE mapping a large page at a higher level
|
|
|
|
* than the target, that SPTE must be cleared and replaced
|
|
|
|
* with a non-leaf SPTE.
|
|
|
|
*/
|
|
|
|
if (is_shadow_present_pte(iter.old_spte) &&
|
|
|
|
is_large_pte(iter.old_spte)) {
|
2021-02-03 02:57:27 +08:00
|
|
|
if (!tdp_mmu_zap_spte_atomic(vcpu->kvm, &iter))
|
2021-02-03 02:57:26 +08:00
|
|
|
break;
|
2020-10-15 02:26:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The iter must explicitly re-read the spte here
|
|
|
|
* because the new value informs the !present
|
|
|
|
* path below.
|
|
|
|
*/
|
2021-02-03 02:57:23 +08:00
|
|
|
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
2020-10-15 02:26:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_shadow_present_pte(iter.old_spte)) {
|
2020-10-15 02:26:51 +08:00
|
|
|
sp = alloc_tdp_mmu_page(vcpu, iter.gfn, iter.level);
|
|
|
|
child_pt = sp->spt;
|
2021-02-03 02:57:25 +08:00
|
|
|
|
2020-10-15 02:26:50 +08:00
|
|
|
new_spte = make_nonleaf_spte(child_pt,
|
|
|
|
!shadow_accessed_mask);
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
if (tdp_mmu_set_spte_atomic(vcpu->kvm, &iter,
|
|
|
|
new_spte)) {
|
|
|
|
tdp_mmu_link_page(vcpu->kvm, sp, true,
|
|
|
|
huge_page_disallowed &&
|
|
|
|
req_level >= iter.level);
|
|
|
|
|
|
|
|
trace_kvm_mmu_get_page(sp, true);
|
|
|
|
} else {
|
|
|
|
tdp_mmu_free_sp(sp);
|
|
|
|
break;
|
|
|
|
}
|
2020-10-15 02:26:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:26 +08:00
|
|
|
if (iter.level != level) {
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
2020-10-15 02:26:50 +08:00
|
|
|
return RET_PF_RETRY;
|
2021-02-03 02:57:23 +08:00
|
|
|
}
|
2020-10-15 02:26:50 +08:00
|
|
|
|
|
|
|
ret = tdp_mmu_map_handle_target_level(vcpu, write, map_writable, &iter,
|
|
|
|
pfn, prefault);
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
2020-10-15 02:26:50 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2020-10-15 02:26:52 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
bool kvm_tdp_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range,
|
|
|
|
bool flush)
|
2020-10-15 02:26:52 +08:00
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
for_each_tdp_mmu_root(kvm, root, range->slot->as_id)
|
|
|
|
flush |= zap_gfn_range(kvm, root, range->start, range->end,
|
2021-04-02 07:37:32 +08:00
|
|
|
range->may_block, flush, false);
|
2020-10-15 02:26:52 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return flush;
|
2020-10-15 02:26:52 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
typedef bool (*tdp_handler_t)(struct kvm *kvm, struct tdp_iter *iter,
|
|
|
|
struct kvm_gfn_range *range);
|
2020-10-15 02:26:52 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
static __always_inline bool kvm_tdp_mmu_handle_gfn(struct kvm *kvm,
|
|
|
|
struct kvm_gfn_range *range,
|
|
|
|
tdp_handler_t handler)
|
2020-10-15 02:26:52 +08:00
|
|
|
{
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
struct tdp_iter iter;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
2021-04-02 08:56:58 +08:00
|
|
|
/*
|
|
|
|
* Don't support rescheduling, none of the MMU notifiers that funnel
|
|
|
|
* into this helper allow blocking; it'd be dead, wasteful code.
|
|
|
|
*/
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
for_each_tdp_mmu_root(kvm, root, range->slot->as_id) {
|
|
|
|
tdp_root_for_each_leaf_pte(iter, root, range->start, range->end)
|
|
|
|
ret |= handler(kvm, &iter, range);
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ret;
|
2020-10-15 02:26:52 +08:00
|
|
|
}
|
2020-10-15 02:26:53 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the SPTEs range of GFNs [start, end) unaccessed and return non-zero
|
|
|
|
* if any of the GFNs in the range have been accessed.
|
|
|
|
*/
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
static bool age_gfn_range(struct kvm *kvm, struct tdp_iter *iter,
|
|
|
|
struct kvm_gfn_range *range)
|
2020-10-15 02:26:53 +08:00
|
|
|
{
|
|
|
|
u64 new_spte = 0;
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
/* If we have a non-accessed entry we don't need to change the pte. */
|
|
|
|
if (!is_accessed_spte(iter->old_spte))
|
|
|
|
return false;
|
2021-02-03 02:57:23 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
new_spte = iter->old_spte;
|
|
|
|
|
|
|
|
if (spte_ad_enabled(new_spte)) {
|
|
|
|
new_spte &= ~shadow_accessed_mask;
|
|
|
|
} else {
|
2020-10-15 02:26:53 +08:00
|
|
|
/*
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
* Capture the dirty status of the page, so that it doesn't get
|
|
|
|
* lost when the SPTE is marked for access tracking.
|
2020-10-15 02:26:53 +08:00
|
|
|
*/
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
if (is_writable_pte(new_spte))
|
|
|
|
kvm_set_pfn_dirty(spte_to_pfn(new_spte));
|
2020-10-15 02:26:53 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
new_spte = mark_spte_for_access_track(new_spte);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
tdp_mmu_set_spte_no_acc_track(kvm, iter, new_spte);
|
2021-02-03 02:57:23 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return true;
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
bool kvm_tdp_mmu_age_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
|
2020-10-15 02:26:53 +08:00
|
|
|
{
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return kvm_tdp_mmu_handle_gfn(kvm, range, age_gfn_range);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
static bool test_age_gfn(struct kvm *kvm, struct tdp_iter *iter,
|
|
|
|
struct kvm_gfn_range *range)
|
2020-10-15 02:26:53 +08:00
|
|
|
{
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return is_accessed_spte(iter->old_spte);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
bool kvm_tdp_mmu_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
2020-10-15 02:26:53 +08:00
|
|
|
{
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return kvm_tdp_mmu_handle_gfn(kvm, range, test_age_gfn);
|
2020-10-15 02:26:53 +08:00
|
|
|
}
|
2020-10-15 02:26:54 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
static bool set_spte_gfn(struct kvm *kvm, struct tdp_iter *iter,
|
|
|
|
struct kvm_gfn_range *range)
|
2020-10-15 02:26:54 +08:00
|
|
|
{
|
|
|
|
u64 new_spte;
|
2021-02-03 02:57:23 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
/* Huge pages aren't expected to be modified without first being zapped. */
|
|
|
|
WARN_ON(pte_huge(range->pte) || range->start + 1 != range->end);
|
2020-10-15 02:26:54 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
if (iter->level != PG_LEVEL_4K ||
|
|
|
|
!is_shadow_present_pte(iter->old_spte))
|
|
|
|
return false;
|
2020-10-15 02:26:54 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
/*
|
|
|
|
* Note, when changing a read-only SPTE, it's not strictly necessary to
|
|
|
|
* zero the SPTE before setting the new PFN, but doing so preserves the
|
|
|
|
* invariant that the PFN of a present * leaf SPTE can never change.
|
|
|
|
* See __handle_changed_spte().
|
|
|
|
*/
|
|
|
|
tdp_mmu_set_spte(kvm, iter, 0);
|
2020-10-15 02:26:54 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
if (!pte_write(range->pte)) {
|
|
|
|
new_spte = kvm_mmu_changed_pte_notifier_make_spte(iter->old_spte,
|
|
|
|
pte_pfn(range->pte));
|
2020-10-15 02:26:54 +08:00
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
tdp_mmu_set_spte(kvm, iter, new_spte);
|
2020-10-15 02:26:54 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
return true;
|
2020-10-15 02:26:54 +08:00
|
|
|
}
|
|
|
|
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
/*
|
|
|
|
* Handle the changed_pte MMU notifier for the TDP MMU.
|
|
|
|
* data is a pointer to the new pte_t mapping the HVA specified by the MMU
|
|
|
|
* notifier.
|
|
|
|
* Returns non-zero if a flush is needed before releasing the MMU lock.
|
|
|
|
*/
|
|
|
|
bool kvm_tdp_mmu_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
2020-10-15 02:26:54 +08:00
|
|
|
{
|
KVM: Move x86's MMU notifier memslot walkers to generic code
Move the hva->gfn lookup for MMU notifiers into common code. Every arch
does a similar lookup, and some arch code is all but identical across
multiple architectures.
In addition to consolidating code, this will allow introducing
optimizations that will benefit all architectures without incurring
multiple walks of the memslots, e.g. by taking mmu_lock if and only if a
relevant range exists in the memslots.
The use of __always_inline to avoid indirect call retpolines, as done by
x86, may also benefit other architectures.
Consolidating the lookups also fixes a wart in x86, where the legacy MMU
and TDP MMU each do their own memslot walks.
Lastly, future enhancements to the memslot implementation, e.g. to add an
interval tree to track host address, will need to touch far less arch
specific code.
MIPS, PPC, and arm64 will be converted one at a time in future patches.
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20210402005658.3024832-3-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-04-02 08:56:50 +08:00
|
|
|
bool flush = kvm_tdp_mmu_handle_gfn(kvm, range, set_spte_gfn);
|
|
|
|
|
|
|
|
/* FIXME: return 'flush' instead of flushing here. */
|
|
|
|
if (flush)
|
|
|
|
kvm_flush_remote_tlbs_with_address(kvm, range->start, 1);
|
|
|
|
|
|
|
|
return false;
|
2020-10-15 02:26:54 +08:00
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
/*
|
|
|
|
* Remove write access from all the SPTEs mapping GFNs [start, end). If
|
|
|
|
* skip_4k is set, SPTEs that map 4k pages, will not be write-protected.
|
|
|
|
* Returns true if an SPTE has been changed and the TLBs need to be flushed.
|
|
|
|
*/
|
|
|
|
static bool wrprot_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
|
|
|
|
gfn_t start, gfn_t end, int min_level)
|
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
u64 new_spte;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
BUG_ON(min_level > KVM_MAX_HUGEPAGE_LEVEL);
|
|
|
|
|
|
|
|
for_each_tdp_pte_min_level(iter, root->spt, root->role.level,
|
|
|
|
min_level, start, end) {
|
2021-04-02 07:37:34 +08:00
|
|
|
retry:
|
|
|
|
if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true))
|
2021-02-03 02:57:20 +08:00
|
|
|
continue;
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
if (!is_shadow_present_pte(iter.old_spte) ||
|
2021-02-03 02:57:21 +08:00
|
|
|
!is_last_spte(iter.old_spte, iter.level) ||
|
|
|
|
!(iter.old_spte & PT_WRITABLE_MASK))
|
2020-10-15 02:26:55 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
new_spte = iter.old_spte & ~PT_WRITABLE_MASK;
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
if (!tdp_mmu_set_spte_atomic_no_dirty_log(kvm, &iter,
|
|
|
|
new_spte)) {
|
|
|
|
/*
|
|
|
|
* The iter must explicitly re-read the SPTE because
|
|
|
|
* the atomic cmpxchg failed.
|
|
|
|
*/
|
|
|
|
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
|
|
|
goto retry;
|
|
|
|
}
|
2020-10-15 02:26:55 +08:00
|
|
|
spte_set = true;
|
|
|
|
}
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2020-10-15 02:26:55 +08:00
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove write access from all the SPTEs mapping GFNs in the memslot. Will
|
|
|
|
* only affect leaf SPTEs down to min_level.
|
|
|
|
* Returns true if an SPTE has been changed and the TLBs need to be flushed.
|
|
|
|
*/
|
|
|
|
bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, struct kvm_memory_slot *slot,
|
|
|
|
int min_level)
|
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
2020-10-15 02:26:55 +08:00
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
for_each_tdp_mmu_root_yield_safe(kvm, root, slot->as_id, true)
|
2020-10-15 02:26:55 +08:00
|
|
|
spte_set |= wrprot_gfn_range(kvm, root, slot->base_gfn,
|
|
|
|
slot->base_gfn + slot->npages, min_level);
|
|
|
|
|
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the dirty status of all the SPTEs mapping GFNs in the memslot. If
|
|
|
|
* AD bits are enabled, this will involve clearing the dirty bit on each SPTE.
|
|
|
|
* If AD bits are not enabled, this will require clearing the writable bit on
|
|
|
|
* each SPTE. Returns true if an SPTE has been changed and the TLBs need to
|
|
|
|
* be flushed.
|
|
|
|
*/
|
|
|
|
static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
|
|
|
|
gfn_t start, gfn_t end)
|
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
u64 new_spte;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
tdp_root_for_each_leaf_pte(iter, root, start, end) {
|
2021-04-02 07:37:34 +08:00
|
|
|
retry:
|
|
|
|
if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true))
|
2021-02-03 02:57:20 +08:00
|
|
|
continue;
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
if (spte_ad_need_write_protect(iter.old_spte)) {
|
|
|
|
if (is_writable_pte(iter.old_spte))
|
|
|
|
new_spte = iter.old_spte & ~PT_WRITABLE_MASK;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (iter.old_spte & shadow_dirty_mask)
|
|
|
|
new_spte = iter.old_spte & ~shadow_dirty_mask;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
if (!tdp_mmu_set_spte_atomic_no_dirty_log(kvm, &iter,
|
|
|
|
new_spte)) {
|
|
|
|
/*
|
|
|
|
* The iter must explicitly re-read the SPTE because
|
|
|
|
* the atomic cmpxchg failed.
|
|
|
|
*/
|
|
|
|
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
|
|
|
goto retry;
|
|
|
|
}
|
2020-10-15 02:26:55 +08:00
|
|
|
spte_set = true;
|
|
|
|
}
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2020-10-15 02:26:55 +08:00
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the dirty status of all the SPTEs mapping GFNs in the memslot. If
|
|
|
|
* AD bits are enabled, this will involve clearing the dirty bit on each SPTE.
|
|
|
|
* If AD bits are not enabled, this will require clearing the writable bit on
|
|
|
|
* each SPTE. Returns true if an SPTE has been changed and the TLBs need to
|
|
|
|
* be flushed.
|
|
|
|
*/
|
|
|
|
bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
|
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
2020-10-15 02:26:55 +08:00
|
|
|
|
2021-04-02 07:37:34 +08:00
|
|
|
for_each_tdp_mmu_root_yield_safe(kvm, root, slot->as_id, true)
|
2020-10-15 02:26:55 +08:00
|
|
|
spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn,
|
|
|
|
slot->base_gfn + slot->npages);
|
|
|
|
|
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clears the dirty status of all the 4k SPTEs mapping GFNs for which a bit is
|
|
|
|
* set in mask, starting at gfn. The given memslot is expected to contain all
|
|
|
|
* the GFNs represented by set bits in the mask. If AD bits are enabled,
|
|
|
|
* clearing the dirty status will involve clearing the dirty bit on each SPTE
|
|
|
|
* or, if AD bits are not enabled, clearing the writable bit on each SPTE.
|
|
|
|
*/
|
|
|
|
static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root,
|
|
|
|
gfn_t gfn, unsigned long mask, bool wrprot)
|
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
u64 new_spte;
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
tdp_root_for_each_leaf_pte(iter, root, gfn + __ffs(mask),
|
|
|
|
gfn + BITS_PER_LONG) {
|
|
|
|
if (!mask)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (iter.level > PG_LEVEL_4K ||
|
|
|
|
!(mask & (1UL << (iter.gfn - gfn))))
|
|
|
|
continue;
|
|
|
|
|
2021-02-03 02:57:22 +08:00
|
|
|
mask &= ~(1UL << (iter.gfn - gfn));
|
|
|
|
|
2020-10-15 02:26:55 +08:00
|
|
|
if (wrprot || spte_ad_need_write_protect(iter.old_spte)) {
|
|
|
|
if (is_writable_pte(iter.old_spte))
|
|
|
|
new_spte = iter.old_spte & ~PT_WRITABLE_MASK;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (iter.old_spte & shadow_dirty_mask)
|
|
|
|
new_spte = iter.old_spte & ~shadow_dirty_mask;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
tdp_mmu_set_spte_no_dirty_log(kvm, &iter, new_spte);
|
|
|
|
}
|
2021-02-03 02:57:23 +08:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
2020-10-15 02:26:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clears the dirty status of all the 4k SPTEs mapping GFNs for which a bit is
|
|
|
|
* set in mask, starting at gfn. The given memslot is expected to contain all
|
|
|
|
* the GFNs represented by set bits in the mask. If AD bits are enabled,
|
|
|
|
* clearing the dirty status will involve clearing the dirty bit on each SPTE
|
|
|
|
* or, if AD bits are not enabled, clearing the writable bit on each SPTE.
|
|
|
|
*/
|
|
|
|
void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm,
|
|
|
|
struct kvm_memory_slot *slot,
|
|
|
|
gfn_t gfn, unsigned long mask,
|
|
|
|
bool wrprot)
|
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
|
2021-02-03 02:57:24 +08:00
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2021-03-26 10:19:45 +08:00
|
|
|
for_each_tdp_mmu_root(kvm, root, slot->as_id)
|
2020-10-15 02:26:55 +08:00
|
|
|
clear_dirty_pt_masked(kvm, root, gfn, mask, wrprot);
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:56 +08:00
|
|
|
/*
|
2021-02-03 02:57:16 +08:00
|
|
|
* Clear leaf entries which could be replaced by large mappings, for
|
|
|
|
* GFNs within the slot.
|
2020-10-15 02:26:56 +08:00
|
|
|
*/
|
2021-03-26 10:19:40 +08:00
|
|
|
static bool zap_collapsible_spte_range(struct kvm *kvm,
|
2020-10-15 02:26:56 +08:00
|
|
|
struct kvm_mmu_page *root,
|
2021-04-02 07:37:24 +08:00
|
|
|
const struct kvm_memory_slot *slot,
|
2021-03-26 10:19:40 +08:00
|
|
|
bool flush)
|
2020-10-15 02:26:56 +08:00
|
|
|
{
|
2021-02-13 08:50:06 +08:00
|
|
|
gfn_t start = slot->base_gfn;
|
|
|
|
gfn_t end = start + slot->npages;
|
2020-10-15 02:26:56 +08:00
|
|
|
struct tdp_iter iter;
|
|
|
|
kvm_pfn_t pfn;
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:56 +08:00
|
|
|
tdp_root_for_each_pte(iter, root, start, end) {
|
2021-04-02 07:37:33 +08:00
|
|
|
retry:
|
|
|
|
if (tdp_mmu_iter_cond_resched(kvm, &iter, flush, true)) {
|
2021-03-26 10:19:40 +08:00
|
|
|
flush = false;
|
2021-02-03 02:57:20 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:56 +08:00
|
|
|
if (!is_shadow_present_pte(iter.old_spte) ||
|
2021-02-03 02:57:16 +08:00
|
|
|
!is_last_spte(iter.old_spte, iter.level))
|
2020-10-15 02:26:56 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
pfn = spte_to_pfn(iter.old_spte);
|
|
|
|
if (kvm_is_reserved_pfn(pfn) ||
|
2021-02-13 08:50:06 +08:00
|
|
|
iter.level >= kvm_mmu_max_mapping_level(kvm, slot, iter.gfn,
|
|
|
|
pfn, PG_LEVEL_NUM))
|
2020-10-15 02:26:56 +08:00
|
|
|
continue;
|
|
|
|
|
2021-04-02 07:37:33 +08:00
|
|
|
if (!tdp_mmu_zap_spte_atomic(kvm, &iter)) {
|
|
|
|
/*
|
|
|
|
* The iter must explicitly re-read the SPTE because
|
|
|
|
* the atomic cmpxchg failed.
|
|
|
|
*/
|
|
|
|
iter.old_spte = READ_ONCE(*rcu_dereference(iter.sptep));
|
|
|
|
goto retry;
|
|
|
|
}
|
2021-03-26 10:19:40 +08:00
|
|
|
flush = true;
|
2020-10-15 02:26:56 +08:00
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
2021-03-26 10:19:40 +08:00
|
|
|
|
|
|
|
return flush;
|
2020-10-15 02:26:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear non-leaf entries (and free associated page tables) which could
|
|
|
|
* be replaced by large mappings, for GFNs within the slot.
|
|
|
|
*/
|
2021-03-26 10:19:42 +08:00
|
|
|
bool kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
2021-04-02 07:37:24 +08:00
|
|
|
const struct kvm_memory_slot *slot,
|
|
|
|
bool flush)
|
2020-10-15 02:26:56 +08:00
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
|
2021-04-02 07:37:33 +08:00
|
|
|
lockdep_assert_held_read(&kvm->mmu_lock);
|
2020-10-15 02:26:56 +08:00
|
|
|
|
2021-04-02 07:37:33 +08:00
|
|
|
for_each_tdp_mmu_root_yield_safe(kvm, root, slot->as_id, true)
|
2021-03-26 10:19:40 +08:00
|
|
|
flush = zap_collapsible_spte_range(kvm, root, slot, flush);
|
|
|
|
|
2021-03-26 10:19:42 +08:00
|
|
|
return flush;
|
2020-10-15 02:26:56 +08:00
|
|
|
}
|
2020-10-15 02:26:57 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Removes write access on the last level SPTE mapping this GFN and unsets the
|
2021-02-26 04:47:43 +08:00
|
|
|
* MMU-writable bit to ensure future writes continue to be intercepted.
|
2020-10-15 02:26:57 +08:00
|
|
|
* Returns true if an SPTE was set and a TLB flush is needed.
|
|
|
|
*/
|
|
|
|
static bool write_protect_gfn(struct kvm *kvm, struct kvm_mmu_page *root,
|
|
|
|
gfn_t gfn)
|
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
u64 new_spte;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:57 +08:00
|
|
|
tdp_root_for_each_leaf_pte(iter, root, gfn, gfn + 1) {
|
|
|
|
if (!is_writable_pte(iter.old_spte))
|
|
|
|
break;
|
|
|
|
|
|
|
|
new_spte = iter.old_spte &
|
2021-02-26 04:47:43 +08:00
|
|
|
~(PT_WRITABLE_MASK | shadow_mmu_writable_mask);
|
2020-10-15 02:26:57 +08:00
|
|
|
|
|
|
|
tdp_mmu_set_spte(kvm, &iter, new_spte);
|
|
|
|
spte_set = true;
|
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2020-10-15 02:26:57 +08:00
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Removes write access on the last level SPTE mapping this GFN and unsets the
|
2021-02-26 04:47:43 +08:00
|
|
|
* MMU-writable bit to ensure future writes continue to be intercepted.
|
2020-10-15 02:26:57 +08:00
|
|
|
* Returns true if an SPTE was set and a TLB flush is needed.
|
|
|
|
*/
|
|
|
|
bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
|
|
|
|
struct kvm_memory_slot *slot, gfn_t gfn)
|
|
|
|
{
|
|
|
|
struct kvm_mmu_page *root;
|
|
|
|
bool spte_set = false;
|
|
|
|
|
2021-02-03 02:57:24 +08:00
|
|
|
lockdep_assert_held_write(&kvm->mmu_lock);
|
2021-03-26 10:19:45 +08:00
|
|
|
for_each_tdp_mmu_root(kvm, root, slot->as_id)
|
2020-10-15 02:26:57 +08:00
|
|
|
spte_set |= write_protect_gfn(kvm, root, gfn);
|
2021-03-26 10:19:45 +08:00
|
|
|
|
2020-10-15 02:26:57 +08:00
|
|
|
return spte_set;
|
|
|
|
}
|
|
|
|
|
2020-10-15 02:26:58 +08:00
|
|
|
/*
|
|
|
|
* Return the level of the lowest level SPTE added to sptes.
|
|
|
|
* That SPTE may be non-present.
|
|
|
|
*/
|
2020-12-18 08:31:37 +08:00
|
|
|
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
|
|
|
|
int *root_level)
|
2020-10-15 02:26:58 +08:00
|
|
|
{
|
|
|
|
struct tdp_iter iter;
|
|
|
|
struct kvm_mmu *mmu = vcpu->arch.mmu;
|
|
|
|
gfn_t gfn = addr >> PAGE_SHIFT;
|
2020-12-18 08:31:36 +08:00
|
|
|
int leaf = -1;
|
2020-10-15 02:26:58 +08:00
|
|
|
|
2020-12-18 08:31:37 +08:00
|
|
|
*root_level = vcpu->arch.mmu->shadow_root_level;
|
2020-10-15 02:26:58 +08:00
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2020-10-15 02:26:58 +08:00
|
|
|
tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
|
|
|
|
leaf = iter.level;
|
2020-12-18 08:31:38 +08:00
|
|
|
sptes[leaf] = iter.old_spte;
|
2020-10-15 02:26:58 +08:00
|
|
|
}
|
|
|
|
|
2021-02-03 02:57:23 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2020-10-15 02:26:58 +08:00
|
|
|
return leaf;
|
|
|
|
}
|