Commit 2e65e842 authored by David Matlack's avatar David Matlack Committed by Paolo Bonzini

KVM: x86/mmu: Derive shadow MMU page role from parent

Instead of computing the shadow page role from scratch for every new
page, derive most of the information from the parent shadow page.  This
eliminates the dependency on the vCPU root role to allocate shadow page
tables, and reduces the number of parameters to kvm_mmu_get_page().

Preemptively split out the role calculation to a separate function for
use in a following commit.

Note that when calculating the MMU root role, we can take
@role.passthrough, @role.direct, and @role.access directly from
@vcpu->arch.mmu->root_role. Only @role.level and @role.quadrant still
must be overridden for PAE page directories, when shadowing 32-bit
guest page tables with PAE page tables.

No functional change intended.
Reviewed-by: default avatarPeter Xu <peterx@redhat.com>
Signed-off-by: default avatarDavid Matlack <dmatlack@google.com>
Message-Id: <20220516232138.1783324-5-dmatlack@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 86938ab6
...@@ -1993,49 +1993,15 @@ static void clear_sp_write_flooding_count(u64 *spte) ...@@ -1993,49 +1993,15 @@ static void clear_sp_write_flooding_count(u64 *spte)
__clear_sp_write_flooding_count(sptep_to_sp(spte)); __clear_sp_write_flooding_count(sptep_to_sp(spte));
} }
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, gfn_t gfn,
gfn_t gfn, union kvm_mmu_page_role role)
gva_t gaddr,
unsigned level,
bool direct,
unsigned int access)
{ {
union kvm_mmu_page_role role;
struct hlist_head *sp_list; struct hlist_head *sp_list;
unsigned quadrant;
struct kvm_mmu_page *sp; struct kvm_mmu_page *sp;
int ret; int ret;
int collisions = 0; int collisions = 0;
LIST_HEAD(invalid_list); LIST_HEAD(invalid_list);
role = vcpu->arch.mmu->root_role;
role.level = level;
role.direct = direct;
role.access = access;
if (role.has_4_byte_gpte) {
/*
* If the guest has 4-byte PTEs then that means it's using 32-bit,
* 2-level, non-PAE paging. KVM shadows such guests with PAE paging
* (i.e. 8-byte PTEs). The difference in PTE size means that KVM must
* shadow each guest page table with multiple shadow page tables, which
* requires extra bookkeeping in the role.
*
* Specifically, to shadow the guest's page directory (which covers a
* 4GiB address space), KVM uses 4 PAE page directories, each mapping
* 1GiB of the address space. @role.quadrant encodes which quarter of
* the address space each maps.
*
* To shadow the guest's page tables (which each map a 4MiB region), KVM
* uses 2 PAE page tables, each mapping a 2MiB region. For these,
* @role.quadrant encodes which half of the region they map.
*/
quadrant = gaddr >> (PAGE_SHIFT + (SPTE_LEVEL_BITS * level));
quadrant &= (1 << level) - 1;
role.quadrant = quadrant;
}
if (level <= vcpu->arch.mmu->cpu_role.base.level)
role.passthrough = 0;
sp_list = &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; sp_list = &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)];
for_each_valid_sp(vcpu->kvm, sp, sp_list) { for_each_valid_sp(vcpu->kvm, sp, sp_list) {
if (sp->gfn != gfn) { if (sp->gfn != gfn) {
...@@ -2053,7 +2019,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, ...@@ -2053,7 +2019,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
* Unsync pages must not be left as is, because the new * Unsync pages must not be left as is, because the new
* upper-level page will be write-protected. * upper-level page will be write-protected.
*/ */
if (level > PG_LEVEL_4K && sp->unsync) if (role.level > PG_LEVEL_4K && sp->unsync)
kvm_mmu_prepare_zap_page(vcpu->kvm, sp, kvm_mmu_prepare_zap_page(vcpu->kvm, sp,
&invalid_list); &invalid_list);
continue; continue;
...@@ -2094,14 +2060,14 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, ...@@ -2094,14 +2060,14 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
++vcpu->kvm->stat.mmu_cache_miss; ++vcpu->kvm->stat.mmu_cache_miss;
sp = kvm_mmu_alloc_page(vcpu, direct); sp = kvm_mmu_alloc_page(vcpu, role.direct);
sp->gfn = gfn; sp->gfn = gfn;
sp->role = role; sp->role = role;
hlist_add_head(&sp->hash_link, sp_list); hlist_add_head(&sp->hash_link, sp_list);
if (sp_has_gptes(sp)) { if (sp_has_gptes(sp)) {
account_shadowed(vcpu->kvm, sp); account_shadowed(vcpu->kvm, sp);
if (level == PG_LEVEL_4K && kvm_vcpu_write_protect_gfn(vcpu, gfn)) if (role.level == PG_LEVEL_4K && kvm_vcpu_write_protect_gfn(vcpu, gfn))
kvm_flush_remote_tlbs_with_address(vcpu->kvm, gfn, 1); kvm_flush_remote_tlbs_with_address(vcpu->kvm, gfn, 1);
} }
trace_kvm_mmu_get_page(sp, true); trace_kvm_mmu_get_page(sp, true);
...@@ -2113,6 +2079,55 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, ...@@ -2113,6 +2079,55 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
return sp; return sp;
} }
static union kvm_mmu_page_role kvm_mmu_child_role(u64 *sptep, bool direct, unsigned int access)
{
struct kvm_mmu_page *parent_sp = sptep_to_sp(sptep);
union kvm_mmu_page_role role;
role = parent_sp->role;
role.level--;
role.access = access;
role.direct = direct;
role.passthrough = 0;
/*
* If the guest has 4-byte PTEs then that means it's using 32-bit,
* 2-level, non-PAE paging. KVM shadows such guests with PAE paging
* (i.e. 8-byte PTEs). The difference in PTE size means that KVM must
* shadow each guest page table with multiple shadow page tables, which
* requires extra bookkeeping in the role.
*
* Specifically, to shadow the guest's page directory (which covers a
* 4GiB address space), KVM uses 4 PAE page directories, each mapping
* 1GiB of the address space. @role.quadrant encodes which quarter of
* the address space each maps.
*
* To shadow the guest's page tables (which each map a 4MiB region), KVM
* uses 2 PAE page tables, each mapping a 2MiB region. For these,
* @role.quadrant encodes which half of the region they map.
*
* Note, the 4 PAE page directories are pre-allocated and the quadrant
* assigned in mmu_alloc_root(). So only page tables need to be handled
* here.
*/
if (role.has_4_byte_gpte) {
WARN_ON_ONCE(role.level != PG_LEVEL_4K);
role.quadrant = (sptep - parent_sp->spt) % 2;
}
return role;
}
static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu,
u64 *sptep, gfn_t gfn,
bool direct, unsigned int access)
{
union kvm_mmu_page_role role;
role = kvm_mmu_child_role(sptep, direct, access);
return kvm_mmu_get_page(vcpu, gfn, role);
}
static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterator, static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterator,
struct kvm_vcpu *vcpu, hpa_t root, struct kvm_vcpu *vcpu, hpa_t root,
u64 addr) u64 addr)
...@@ -2964,8 +2979,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) ...@@ -2964,8 +2979,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (is_shadow_present_pte(*it.sptep)) if (is_shadow_present_pte(*it.sptep))
continue; continue;
sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr, sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, true, ACC_ALL);
it.level - 1, true, ACC_ALL);
link_shadow_page(vcpu, it.sptep, sp); link_shadow_page(vcpu, it.sptep, sp);
if (fault->is_tdp && fault->huge_page_disallowed && if (fault->is_tdp && fault->huge_page_disallowed &&
...@@ -3368,13 +3382,18 @@ static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn) ...@@ -3368,13 +3382,18 @@ static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn)
return ret; return ret;
} }
static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, gva_t gva, static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, int quadrant,
u8 level) u8 level)
{ {
bool direct = vcpu->arch.mmu->root_role.direct; union kvm_mmu_page_role role = vcpu->arch.mmu->root_role;
struct kvm_mmu_page *sp; struct kvm_mmu_page *sp;
sp = kvm_mmu_get_page(vcpu, gfn, gva, level, direct, ACC_ALL); role.level = level;
if (role.has_4_byte_gpte)
role.quadrant = quadrant;
sp = kvm_mmu_get_page(vcpu, gfn, role);
++sp->root_count; ++sp->root_count;
return __pa(sp->spt); return __pa(sp->spt);
...@@ -3408,8 +3427,8 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu) ...@@ -3408,8 +3427,8 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
WARN_ON_ONCE(IS_VALID_PAE_ROOT(mmu->pae_root[i])); WARN_ON_ONCE(IS_VALID_PAE_ROOT(mmu->pae_root[i]));
root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT), root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT), i,
i << 30, PT32_ROOT_LEVEL); PT32_ROOT_LEVEL);
mmu->pae_root[i] = root | PT_PRESENT_MASK | mmu->pae_root[i] = root | PT_PRESENT_MASK |
shadow_me_value; shadow_me_value;
} }
...@@ -3578,8 +3597,7 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) ...@@ -3578,8 +3597,7 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
root_gfn = pdptrs[i] >> PAGE_SHIFT; root_gfn = pdptrs[i] >> PAGE_SHIFT;
} }
root = mmu_alloc_root(vcpu, root_gfn, i << 30, root = mmu_alloc_root(vcpu, root_gfn, i, PT32_ROOT_LEVEL);
PT32_ROOT_LEVEL);
mmu->pae_root[i] = root | pm_mask; mmu->pae_root[i] = root | pm_mask;
} }
......
...@@ -654,8 +654,9 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, ...@@ -654,8 +654,9 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
if (!is_shadow_present_pte(*it.sptep)) { if (!is_shadow_present_pte(*it.sptep)) {
table_gfn = gw->table_gfn[it.level - 2]; table_gfn = gw->table_gfn[it.level - 2];
access = gw->pt_access[it.level - 2]; access = gw->pt_access[it.level - 2];
sp = kvm_mmu_get_page(vcpu, table_gfn, fault->addr, sp = kvm_mmu_get_child_sp(vcpu, it.sptep, table_gfn,
it.level-1, false, access); false, access);
/* /*
* We must synchronize the pagetable before linking it * We must synchronize the pagetable before linking it
* because the guest doesn't need to flush tlb when * because the guest doesn't need to flush tlb when
...@@ -711,8 +712,8 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, ...@@ -711,8 +712,8 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
drop_large_spte(vcpu, it.sptep); drop_large_spte(vcpu, it.sptep);
if (!is_shadow_present_pte(*it.sptep)) { if (!is_shadow_present_pte(*it.sptep)) {
sp = kvm_mmu_get_page(vcpu, base_gfn, fault->addr, sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn,
it.level - 1, true, direct_access); true, direct_access);
link_shadow_page(vcpu, it.sptep, sp); link_shadow_page(vcpu, it.sptep, sp);
if (fault->huge_page_disallowed && if (fault->huge_page_disallowed &&
fault->req_level >= it.level) fault->req_level >= it.level)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment