Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 59 additions & 35 deletions arch/x86/kvm/mmu/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1169,29 +1169,6 @@ static void drop_spte(struct kvm *kvm, u64 *sptep)
rmap_remove(kvm, sptep);
}


static bool __drop_large_spte(struct kvm *kvm, u64 *sptep)
{
if (is_large_pte(*sptep)) {
WARN_ON(sptep_to_sp(sptep)->role.level == PG_LEVEL_4K);
drop_spte(kvm, sptep);
--kvm->stat.lpages;
return true;
}

return false;
}

static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep)
{
if (__drop_large_spte(vcpu->kvm, sptep)) {
struct kvm_mmu_page *sp = sptep_to_sp(sptep);

kvm_flush_remote_tlbs_with_address(vcpu->kvm, sp->gfn,
KVM_PAGES_PER_HPAGE(sp->role.level));
}
}

/*
* Write-protect on the specified @sptep, @pt_protect indicates whether
* spte write-protection is caused by protecting shadow page table.
Expand Down Expand Up @@ -2076,20 +2053,13 @@ static void clear_sp_write_flooding_count(u64 *spte)
__clear_sp_write_flooding_count(sptep_to_sp(spte));
}

static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
gfn_t gfn,
gva_t gaddr,
unsigned level,
int direct,
unsigned int access)
static union kvm_mmu_page_role
kvm_mmu_child_role(struct kvm_vcpu *vcpu, gva_t gaddr, unsigned int level,
int direct, unsigned int access)
{
bool direct_mmu = vcpu->arch.mmu->direct_map;
union kvm_mmu_page_role role;
struct hlist_head *sp_list;
unsigned quadrant;
struct kvm_mmu_page *sp;
int collisions = 0;
LIST_HEAD(invalid_list);

role = vcpu->arch.mmu->mmu_role.base;
role.level = level;
Expand All @@ -2103,6 +2073,25 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
role.quadrant = quadrant;
}

return role;
}

static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
gfn_t gfn,
gva_t gaddr,
unsigned level,
int direct,
unsigned int access)
{
bool direct_mmu = vcpu->arch.mmu->direct_map;
union kvm_mmu_page_role role;
struct hlist_head *sp_list;
struct kvm_mmu_page *sp;
int collisions = 0;
LIST_HEAD(invalid_list);

role = kvm_mmu_child_role(vcpu, gaddr, level, direct, access);

sp_list = &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)];
for_each_valid_sp(vcpu->kvm, sp, sp_list) {
if (sp->gfn != gfn) {
Expand Down Expand Up @@ -2312,6 +2301,40 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
return 0;
}

static bool kvm_mmu_child_sp_exists(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn,
gva_t gaddr, unsigned int level, int direct,
unsigned int access)
{
union kvm_mmu_page_role role;
struct kvm_mmu_page *child;

if (!is_shadow_present_pte(*sptep) || is_large_pte(*sptep))
return false;

child = to_shadow_page(*sptep & PT64_BASE_ADDR_MASK);
if (!child)
return false;

role = kvm_mmu_child_role(vcpu, gaddr, level, direct, access);
return child->gfn == gfn && child->role.word == role.word;
}

static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep)
{
struct kvm *kvm = vcpu->kvm;
struct kvm_mmu_page *parent_sp;
LIST_HEAD(invalid_list);

if (!is_shadow_present_pte(*sptep))
return;

parent_sp = sptep_to_sp(sptep);
WARN_ON_ONCE(parent_sp->role.level == PG_LEVEL_4K);

mmu_page_zap_pte(kvm, parent_sp, sptep, &invalid_list);
kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, true);
}

static int kvm_mmu_page_unlink_children(struct kvm *kvm,
struct kvm_mmu_page *sp,
struct list_head *invalid_list)
Expand Down Expand Up @@ -2997,10 +3020,11 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,
if (it.level == level)
break;

drop_large_spte(vcpu, it.sptep);
if (is_shadow_present_pte(*it.sptep))
if (kvm_mmu_child_sp_exists(vcpu, it.sptep, base_gfn, it.addr,
it.level - 1, true, ACC_ALL))
continue;

drop_large_spte(vcpu, it.sptep);
sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr,
it.level - 1, true, ACC_ALL);

Expand Down
15 changes: 8 additions & 7 deletions arch/x86/kvm/mmu/paging_tmpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -659,12 +659,13 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr,
gfn_t table_gfn;

clear_sp_write_flooding_count(it.sptep);
drop_large_spte(vcpu, it.sptep);

table_gfn = gw->table_gfn[it.level - 2];
access = gw->pt_access[it.level - 2];
sp = NULL;
if (!is_shadow_present_pte(*it.sptep)) {
table_gfn = gw->table_gfn[it.level - 2];
access = gw->pt_access[it.level - 2];
if (!kvm_mmu_child_sp_exists(vcpu, it.sptep, table_gfn, addr,
it.level - 1, false, access)) {
drop_large_spte(vcpu, it.sptep);
sp = kvm_mmu_get_page(vcpu, table_gfn, addr,
it.level-1, false, access);
/*
Expand Down Expand Up @@ -721,9 +722,9 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr,

validate_direct_spte(vcpu, it.sptep, direct_access);

drop_large_spte(vcpu, it.sptep);

if (!is_shadow_present_pte(*it.sptep)) {
if (!kvm_mmu_child_sp_exists(vcpu, it.sptep, base_gfn, addr,
it.level - 1, true, direct_access)) {
drop_large_spte(vcpu, it.sptep);
sp = kvm_mmu_get_page(vcpu, base_gfn, addr,
it.level - 1, true, direct_access);
link_shadow_page(vcpu, it.sptep, sp);
Expand Down
Loading