[PATCH 01/20] KVM: arm64: Prefault host stage-2 entries on block split
Vincent Donnefort <[email protected]> Mon, 3 Aug 2026 11:08:45 +0100
| Newsgroups | dev.linux.lists.kvmarm,org.infradead.lists.linux-arm-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Keir Fraser <[email protected]> For the host stage-2 that is idmap, we can avoid lazy-mapping on block splitting by propagate existing valid mappings to neighbouring entries of the new table. Co-developed-by: Quentin Perret <[email protected]> Signed-off-by: Keir Fraser <[email protected]> Signed-off-by: Vincent Donnefort <[email protected]> diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index b74dd5ce1efd..34f78a58cded 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1030,10 +1030,39 @@ static int stage2_map_walk_table_pre(const struct kvm_pgtable_visit_ctx *ctx, return 0; } +static void stage2_map_prefault_idmap(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t *ptep) +{ + kvm_pte_t block_pte = ctx->old; + u64 pa; + int i; + + if (!kvm_pte_valid(block_pte)) + return; + + pa = ALIGN_DOWN(ctx->addr, kvm_granule_size(ctx->level)); + for (i = 0; i < PTRS_PER_PTE; ++i, ++ptep, pa += kvm_granule_size(ctx->level + 1)) { + kvm_pte_t pte = kvm_init_valid_leaf_pte(pa, block_pte, ctx->level + 1); + + /* + * Skip ptes in the range being modified by the caller if we're + * installing last level entries. Otherwise, we need to + * temporarily put in a valid mapping to make sure the + * prefaulting logic is triggered on the next + * stage2_map_walk_leaf(). This adds an unnecessary TLBI as + * we'll presumably re-break the freshly installed block, but + * that should happen very infrequently. + */ + if ((ctx->level < (KVM_PGTABLE_LAST_LEVEL - 1)) || + (pa < ctx->addr) || (pa >= ctx->end)) + *ptep = pte; + } +} + static int stage2_map_walk_leaf(const struct kvm_pgtable_visit_ctx *ctx, struct stage2_map_data *data) { struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops; + struct kvm_pgtable *pgt = data->mmu->pgt; kvm_pte_t *childp, new; int ret; @@ -1051,6 +1080,9 @@ static int stage2_map_walk_leaf(const struct kvm_pgtable_visit_ctx *ctx, if (!childp) return -ENOMEM; + if (pgt->flags & KVM_PGTABLE_S2_IDMAP) + stage2_map_prefault_idmap(ctx, childp); + if (!stage2_try_break_pte(ctx, data->mmu)) { mm_ops->put_page(childp); return -EAGAIN; -- 2.55.0.508.g3f0d502094-goog