mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
KVM: arm64: Split kvm_pgtable_stage2_destroy()
Split kvm_pgtable_stage2_destroy() into two:
- kvm_pgtable_stage2_destroy_range(), that performs the
page-table walk and free the entries over a range of addresses.
- kvm_pgtable_stage2_destroy_pgd(), that frees the PGD.
This refactoring enables subsequent patches to free large page-tables
in chunks, calling cond_resched() between each chunk, to yield the
CPU as necessary.
Existing callers of kvm_pgtable_stage2_destroy(), that probably cannot
take advantage of this (such as nVMHE), will continue to function as is.
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Suggested-by: Oliver Upton <oupton@kernel.org>
Link: https://msgid.link/20251113052452.975081-3-rananta@google.com
Signed-off-by: Oliver Upton <oupton@kernel.org>
This commit is contained in:
committed by
Oliver Upton
parent
156f70afcf
commit
d68d66e57e
@@ -355,6 +355,11 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke
|
|||||||
return pteref;
|
return pteref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline kvm_pte_t *kvm_dereference_pteref_raw(kvm_pteref_t pteref)
|
||||||
|
{
|
||||||
|
return pteref;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
|
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -384,6 +389,11 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke
|
|||||||
return rcu_dereference_check(pteref, !(walker->flags & KVM_PGTABLE_WALK_SHARED));
|
return rcu_dereference_check(pteref, !(walker->flags & KVM_PGTABLE_WALK_SHARED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline kvm_pte_t *kvm_dereference_pteref_raw(kvm_pteref_t pteref)
|
||||||
|
{
|
||||||
|
return rcu_dereference_raw(pteref);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
|
static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
|
||||||
{
|
{
|
||||||
if (walker->flags & KVM_PGTABLE_WALK_SHARED)
|
if (walker->flags & KVM_PGTABLE_WALK_SHARED)
|
||||||
@@ -551,6 +561,26 @@ static inline int kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2
|
|||||||
*/
|
*/
|
||||||
void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
|
void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_pgtable_stage2_destroy_range() - Destroy the unlinked range of addresses.
|
||||||
|
* @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*().
|
||||||
|
* @addr: Intermediate physical address at which to place the mapping.
|
||||||
|
* @size: Size of the mapping.
|
||||||
|
*
|
||||||
|
* The page-table is assumed to be unreachable by any hardware walkers prior
|
||||||
|
* to freeing and therefore no TLB invalidation is performed.
|
||||||
|
*/
|
||||||
|
void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
|
||||||
|
u64 addr, u64 size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_pgtable_stage2_destroy_pgd() - Destroy the PGD of guest stage-2 page-table.
|
||||||
|
* @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*().
|
||||||
|
*
|
||||||
|
* It is assumed that the rest of the page-table is freed before this operation.
|
||||||
|
*/
|
||||||
|
void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_pgtable_stage2_free_unlinked() - Free an unlinked stage-2 paging structure.
|
* kvm_pgtable_stage2_free_unlinked() - Free an unlinked stage-2 paging structure.
|
||||||
* @mm_ops: Memory management callbacks.
|
* @mm_ops: Memory management callbacks.
|
||||||
|
|||||||
@@ -180,7 +180,9 @@ struct pkvm_mapping {
|
|||||||
|
|
||||||
int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
||||||
struct kvm_pgtable_mm_ops *mm_ops);
|
struct kvm_pgtable_mm_ops *mm_ops);
|
||||||
void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
|
void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
|
||||||
|
u64 addr, u64 size);
|
||||||
|
void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt);
|
||||||
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
|
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
|
||||||
enum kvm_pgtable_prot prot, void *mc,
|
enum kvm_pgtable_prot prot, void *mc,
|
||||||
enum kvm_pgtable_walk_flags flags);
|
enum kvm_pgtable_walk_flags flags);
|
||||||
|
|||||||
@@ -1577,21 +1577,38 @@ static int stage2_free_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)
|
void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
|
||||||
|
u64 addr, u64 size)
|
||||||
{
|
{
|
||||||
size_t pgd_sz;
|
|
||||||
struct kvm_pgtable_walker walker = {
|
struct kvm_pgtable_walker walker = {
|
||||||
.cb = stage2_free_walker,
|
.cb = stage2_free_walker,
|
||||||
.flags = KVM_PGTABLE_WALK_LEAF |
|
.flags = KVM_PGTABLE_WALK_LEAF |
|
||||||
KVM_PGTABLE_WALK_TABLE_POST,
|
KVM_PGTABLE_WALK_TABLE_POST,
|
||||||
};
|
};
|
||||||
|
|
||||||
WARN_ON(kvm_pgtable_walk(pgt, 0, BIT(pgt->ia_bits), &walker));
|
WARN_ON(kvm_pgtable_walk(pgt, addr, size, &walker));
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
|
||||||
|
{
|
||||||
|
size_t pgd_sz;
|
||||||
|
|
||||||
pgd_sz = kvm_pgd_pages(pgt->ia_bits, pgt->start_level) * PAGE_SIZE;
|
pgd_sz = kvm_pgd_pages(pgt->ia_bits, pgt->start_level) * PAGE_SIZE;
|
||||||
pgt->mm_ops->free_pages_exact(kvm_dereference_pteref(&walker, pgt->pgd), pgd_sz);
|
|
||||||
|
/*
|
||||||
|
* Since the pgtable is unlinked at this point, and not shared with
|
||||||
|
* other walkers, safely deference pgd with kvm_dereference_pteref_raw()
|
||||||
|
*/
|
||||||
|
pgt->mm_ops->free_pages_exact(kvm_dereference_pteref_raw(pgt->pgd), pgd_sz);
|
||||||
pgt->pgd = NULL;
|
pgt->pgd = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)
|
||||||
|
{
|
||||||
|
kvm_pgtable_stage2_destroy_range(pgt, 0, BIT(pgt->ia_bits));
|
||||||
|
kvm_pgtable_stage2_destroy_pgd(pgt);
|
||||||
|
}
|
||||||
|
|
||||||
void kvm_pgtable_stage2_free_unlinked(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, s8 level)
|
void kvm_pgtable_stage2_free_unlinked(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, s8 level)
|
||||||
{
|
{
|
||||||
kvm_pteref_t ptep = (kvm_pteref_t)pgtable;
|
kvm_pteref_t ptep = (kvm_pteref_t)pgtable;
|
||||||
|
|||||||
@@ -904,6 +904,14 @@ static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_stage2_destroy(struct kvm_pgtable *pgt)
|
||||||
|
{
|
||||||
|
unsigned int ia_bits = VTCR_EL2_IPA(pgt->mmu->vtcr);
|
||||||
|
|
||||||
|
KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, 0, BIT(ia_bits));
|
||||||
|
KVM_PGT_FN(kvm_pgtable_stage2_destroy_pgd)(pgt);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_init_stage2_mmu - Initialise a S2 MMU structure
|
* kvm_init_stage2_mmu - Initialise a S2 MMU structure
|
||||||
* @kvm: The pointer to the KVM structure
|
* @kvm: The pointer to the KVM structure
|
||||||
@@ -980,7 +988,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_destroy_pgtable:
|
out_destroy_pgtable:
|
||||||
KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt);
|
kvm_stage2_destroy(pgt);
|
||||||
out_free_pgtable:
|
out_free_pgtable:
|
||||||
kfree(pgt);
|
kfree(pgt);
|
||||||
return err;
|
return err;
|
||||||
@@ -1081,7 +1089,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
|
|||||||
write_unlock(&kvm->mmu_lock);
|
write_unlock(&kvm->mmu_lock);
|
||||||
|
|
||||||
if (pgt) {
|
if (pgt) {
|
||||||
KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt);
|
kvm_stage2_destroy(pgt);
|
||||||
kfree(pgt);
|
kfree(pgt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,9 +344,16 @@ static int __pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 start, u64 e
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)
|
void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
|
||||||
|
u64 addr, u64 size)
|
||||||
{
|
{
|
||||||
__pkvm_pgtable_stage2_unmap(pgt, 0, ~(0ULL));
|
__pkvm_pgtable_stage2_unmap(pgt, addr, addr + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
|
||||||
|
{
|
||||||
|
/* Expected to be called after all pKVM mappings have been released. */
|
||||||
|
WARN_ON_ONCE(!RB_EMPTY_ROOT(&pgt->pkvm_mappings.rb_root));
|
||||||
}
|
}
|
||||||
|
|
||||||
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
||||||
|
|||||||
Reference in New Issue
Block a user