Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: Only drop references on empty tables in stage2_free_walker

A subsequent change to the way KVM frees stage-2s will invoke the free
walker on sub-ranges of the VM's IPA space, meaning there's potential
for only partially visiting a table's PTEs.

Split the leaf and table visitors and only drop references on a table
when the page count reaches 1, implying there are no valid PTEs that
need to be visited. Invalidate the table PTE to avoid traversing the
stale reference.

Link: https://msgid.link/20251113052452.975081-2-rananta@google.com
Signed-off-by: Oliver Upton <oupton@kernel.org>

+34 -8
+34 -8
arch/arm64/kvm/hyp/pgtable.c
··· 1535 1535 return kvm_pgd_pages(ia_bits, start_level) * PAGE_SIZE; 1536 1536 } 1537 1537 1538 - static int stage2_free_walker(const struct kvm_pgtable_visit_ctx *ctx, 1539 - enum kvm_pgtable_walk_flags visit) 1538 + static int stage2_free_leaf(const struct kvm_pgtable_visit_ctx *ctx) 1540 1539 { 1541 1540 struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops; 1542 1541 1542 + mm_ops->put_page(ctx->ptep); 1543 + return 0; 1544 + } 1545 + 1546 + static int stage2_free_table_post(const struct kvm_pgtable_visit_ctx *ctx) 1547 + { 1548 + struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops; 1549 + kvm_pte_t *childp = kvm_pte_follow(ctx->old, mm_ops); 1550 + 1551 + if (mm_ops->page_count(childp) != 1) 1552 + return 0; 1553 + 1554 + /* 1555 + * Drop references and clear the now stale PTE to avoid rewalking the 1556 + * freed page table. 1557 + */ 1558 + mm_ops->put_page(ctx->ptep); 1559 + mm_ops->put_page(childp); 1560 + kvm_clear_pte(ctx->ptep); 1561 + return 0; 1562 + } 1563 + 1564 + static int stage2_free_walker(const struct kvm_pgtable_visit_ctx *ctx, 1565 + enum kvm_pgtable_walk_flags visit) 1566 + { 1543 1567 if (!stage2_pte_is_counted(ctx->old)) 1544 1568 return 0; 1545 1569 1546 - mm_ops->put_page(ctx->ptep); 1547 - 1548 - if (kvm_pte_table(ctx->old, ctx->level)) 1549 - mm_ops->put_page(kvm_pte_follow(ctx->old, mm_ops)); 1550 - 1551 - return 0; 1570 + switch (visit) { 1571 + case KVM_PGTABLE_WALK_LEAF: 1572 + return stage2_free_leaf(ctx); 1573 + case KVM_PGTABLE_WALK_TABLE_POST: 1574 + return stage2_free_table_post(ctx); 1575 + default: 1576 + return -EINVAL; 1577 + } 1552 1578 } 1553 1579 1554 1580 void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)