Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size
@ 2026-11-04 12:44 Fuad Tabba
  2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-11-04 12:44 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly, Steffen Eiden,
	Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
	Mark Rutland, Raghavendra Rao Ananta, Vincent Donnefort,
	Fuad Tabba

Hi folks,

Changes since v1 [1]:
  - Run all teardown from stage2_destroy_range() (Oliver, Suzuki)
  - Dropped the Fixes tags (Marc)
  - Dropped the patch moving the teardown into kvm_vm_s2_ops
  - Use gfn_to_gpa() for a mapping's address. (Vincent)

Two patches that make stage-2 teardown take time proportional to what
is mapped, rather than to the size of the IPA space as it has since
4ddfab5436b6.

Under QEMU TCG ('-cpu max', 16K pages with LPA2), destroying even an
empty VM takes about 7 seconds in pKVM. Without pKVM it takes about 19
seconds if the VM requests a 52-bit IPA (crosvm requests the host's
limit). With the series, both take well under a second. I tested only
under QEMU, since I don't have hardware with LPA2.

The first has the page-table walk return where it stopped, so that the
teardown steps over an invalid entry larger than a block-sized chunk in
one go. The second has pKVM return where its next mapping starts.

pKVM gives every VM the host's IPA limit. My series honoring
KVM_VM_TYPE_ARM_IPA_SIZE in pKVM [2] doesn't change that for crosvm,
which requests the limit.

Based on Linux 7.4-rc1 (6628e7d4c6b25).

Cheers,
/fuad

[1] https://lore.kernel.org/all/20261103084742.1302597-1-fuad.tabba@linux.dev/
[2] https://lore.kernel.org/all/20260917091826.2639205-1-fuad.tabba@linux.dev/

Fuad Tabba (2):
  KVM: arm64: Resume stage-2 teardown where the walk stopped
  KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping

 arch/arm64/include/asm/kvm_pgtable.h |  8 ++++++--
 arch/arm64/include/asm/kvm_pkvm.h    |  4 ++--
 arch/arm64/kvm/hyp/pgtable.c         | 15 ++++++++++++---
 arch/arm64/kvm/mmu.c                 |  4 ++--
 arch/arm64/kvm/pkvm.c                | 12 +++++++++---
 5 files changed, 31 insertions(+), 12 deletions(-)


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.47.3



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped
  2026-11-04 12:44 [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Fuad Tabba
@ 2026-11-04 12:44 ` Fuad Tabba
  2026-11-04 18:30   ` Oliver Upton
  2026-11-05 11:07   ` Suzuki K Poulose
  2026-11-04 12:44 ` [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping Fuad Tabba
  2026-11-05 18:59 ` [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Wei-Lin Chang
  2 siblings, 2 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-11-04 12:44 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly, Steffen Eiden,
	Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
	Mark Rutland, Raghavendra Rao Ananta, Vincent Donnefort,
	Fuad Tabba

Since commit 4ddfab5436b6 ("KVM: arm64: Reschedule as needed when
destroying the stage-2 page-tables"), kvm_stage2_destroy() walks the
IPA space one block-sized chunk at a time. The walker steps over an
invalid entry in one go, but the next walk starts at the following
chunk, so an invalid entry larger than a chunk is visited once for every
chunk it covers. Destroying a VM with a 52-bit IPA then takes 2^27 walks
with 16K pages (2^22 with 4K), however little it maps.

Have kvm_pgtable_stage2_destroy_range() return the address its walk
reached, which kvm_pgtable_walk() doesn't expose, and resume from there.
pkvm_pgtable_stage2_destroy_range() returns the end of its range, and
EL2's kvm_pgtable_stage2_destroy() ignores the value.

Suggested-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Suggested-by: Oliver Upton <oupton@kernel.org>
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/include/asm/kvm_pgtable.h |  8 ++++++--
 arch/arm64/include/asm/kvm_pkvm.h    |  4 ++--
 arch/arm64/kvm/hyp/pgtable.c         | 15 ++++++++++++---
 arch/arm64/kvm/mmu.c                 |  4 ++--
 arch/arm64/kvm/pkvm.c                |  8 +++++---
 5 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h
index 1c9c57cd6ae40..20b988e99e074 100644
--- a/arch/arm64/include/asm/kvm_pgtable.h
+++ b/arch/arm64/include/asm/kvm_pgtable.h
@@ -599,9 +599,13 @@ void kvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
  *
  * The page-table is assumed to be unreachable by any hardware walkers prior
  * to freeing and therefore no TLB invalidation is performed.
+ *
+ * Return: the address the walk reached, which can be past @addr + @size
+ * when the range ends within a block or an invalid entry, or @addr + @size
+ * if the walk fails.
  */
-void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
-					u64 addr, u64 size);
+u64 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.
diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
index fd042d2b54624..731632aa24520 100644
--- a/arch/arm64/include/asm/kvm_pkvm.h
+++ b/arch/arm64/include/asm/kvm_pkvm.h
@@ -349,8 +349,8 @@ int __pkvm_handle_smccc_req(struct arm_smccc_res *res);
 
 int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
 			     struct kvm_pgtable_mm_ops *mm_ops);
-void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
-					u64 addr, u64 size);
+u64 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,
 			    enum kvm_pgtable_prot prot, void *mc,
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index 9c83837b184d3..6bb1c0beb4dcc 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -1653,16 +1653,25 @@ static int stage2_free_walker(const struct kvm_pgtable_visit_ctx *ctx,
 	}
 }
 
-void kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
-				       u64 addr, u64 size)
+u64 kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
+				     u64 addr, u64 size)
 {
 	struct kvm_pgtable_walker walker = {
 		.cb	= stage2_free_walker,
 		.flags	= KVM_PGTABLE_WALK_LEAF |
 			  KVM_PGTABLE_WALK_TABLE_POST,
 	};
+	struct kvm_pgtable_walk_data data = {
+		.walker	= &walker,
+		.start	= addr,
+		.addr	= addr,
+		.end	= addr + size,
+	};
 
-	WARN_ON(kvm_pgtable_walk(pgt, addr, size, &walker));
+	if (WARN_ON(_kvm_pgtable_walk(pgt, &data)))
+		return addr + size;
+
+	return data.addr;
 }
 
 void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 1db10d8d6db73..4f1adadeffb8b 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1011,8 +1011,8 @@ static void stage2_destroy_range(struct kvm_pgtable *pgt, phys_addr_t addr,
 
 	do {
 		next = stage2_range_addr_end(addr, end);
-		KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, addr,
-							     next - addr);
+		next = KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, addr, next - addr);
+		next = min(next, end);
 		if (next != end)
 			cond_resched();
 	} while (addr = next, addr != end);
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 016997288fd23..f21c3326dda68 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -499,14 +499,14 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
 	return 0;
 }
 
-void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
-					u64 addr, u64 size)
+u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
+				      u64 addr, u64 size)
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
 	pkvm_handle_t handle = kvm->arch.pkvm.handle;
 
 	if (!handle)
-		return;
+		return addr + size;
 
 	if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) {
 		WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle));
@@ -517,6 +517,8 @@ void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
 		__pkvm_pgtable_stage2_reclaim(pgt, addr, addr + size);
 	else
 		__pkvm_pgtable_stage2_unshare(pgt, addr, addr + size);
+
+	return addr + size;
 }
 
 void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping
  2026-11-04 12:44 [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Fuad Tabba
  2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
@ 2026-11-04 12:44 ` Fuad Tabba
  2026-11-05  9:45   ` Vincent Donnefort
  2026-11-05 18:59 ` [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Wei-Lin Chang
  2 siblings, 1 reply; 10+ messages in thread
From: Fuad Tabba @ 2026-11-04 12:44 UTC (permalink / raw)
  To: Marc Zyngier, Oliver Upton
  Cc: kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly, Steffen Eiden,
	Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
	Mark Rutland, Raghavendra Rao Ananta, Vincent Donnefort,
	Fuad Tabba

pKVM keeps a VM's stage-2 mappings in an interval tree, and
pkvm_pgtable_stage2_destroy_range() releases the ones in its range.
stage2_destroy_range() still calls it for every block-sized chunk of the
IPA space. As pKVM gives every VM the host's IPA limit, a teardown with
LPA2 makes 2^27 calls with 16K pages (2^22 with 4K), however little the
VM maps.

Return the address of the next mapping instead, or U64_MAX when none is
left, so that the teardown skips the space between mappings. No mapping
crosses a chunk boundary, as pKVM maps at most PMD_SIZE, so the next one
starts at or above the end of the range. A VM without a handle doesn't
have anything mapped, so return U64_MAX for it straight away.

Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/kvm/pkvm.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index f21c3326dda68..77315c83c61b3 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -504,9 +504,10 @@ u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
 	pkvm_handle_t handle = kvm->arch.pkvm.handle;
+	struct pkvm_mapping *mapping;
 
 	if (!handle)
-		return addr + size;
+		return U64_MAX;
 
 	if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) {
 		WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle));
@@ -518,7 +519,10 @@ u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
 	else
 		__pkvm_pgtable_stage2_unshare(pgt, addr, addr + size);
 
-	return addr + size;
+	/* Mappings left by a failed release are still in the tree, so look past the range. */
+	mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr + size, U64_MAX);
+
+	return mapping ? gfn_to_gpa(mapping->gfn) : U64_MAX;
 }
 
 void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped
  2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
@ 2026-11-04 18:30   ` Oliver Upton
  2026-11-05  8:07     ` Fuad Tabba
  2026-11-05 11:07   ` Suzuki K Poulose
  1 sibling, 1 reply; 10+ messages in thread
From: Oliver Upton @ 2026-11-04 18:30 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly,
	Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
	Will Deacon, Mark Rutland, Raghavendra Rao Ananta,
	Vincent Donnefort, Fuad Tabba

Hi Fuad,

On Wed, Nov 04, 2026 at 12:44:50PM +0000, Fuad Tabba wrote:
> +u64 kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
> +				     u64 addr, u64 size)

Returning a signed value here would be a good idea so you can percolate
walk failures back up to the caller. Since that WARN_ON() below may as
well be a BUG_ON() in hyp code.

Otherwise, rest looks good.

Thanks,
Oliver

>  {
>  	struct kvm_pgtable_walker walker = {
>  		.cb	= stage2_free_walker,
>  		.flags	= KVM_PGTABLE_WALK_LEAF |
>  			  KVM_PGTABLE_WALK_TABLE_POST,
>  	};
> +	struct kvm_pgtable_walk_data data = {
> +		.walker	= &walker,
> +		.start	= addr,
> +		.addr	= addr,
> +		.end	= addr + size,
> +	};
>  
> -	WARN_ON(kvm_pgtable_walk(pgt, addr, size, &walker));
> +	if (WARN_ON(_kvm_pgtable_walk(pgt, &data)))
> +		return addr + size;
> +
> +	return data.addr;
>  }
>  
>  void kvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 1db10d8d6db73..4f1adadeffb8b 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1011,8 +1011,8 @@ static void stage2_destroy_range(struct kvm_pgtable *pgt, phys_addr_t addr,
>  
>  	do {
>  		next = stage2_range_addr_end(addr, end);
> -		KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, addr,
> -							     next - addr);
> +		next = KVM_PGT_FN(kvm_pgtable_stage2_destroy_range)(pgt, addr, next - addr);
> +		next = min(next, end);
>  		if (next != end)
>  			cond_resched();
>  	} while (addr = next, addr != end);
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 016997288fd23..f21c3326dda68 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -499,14 +499,14 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
>  	return 0;
>  }
>  
> -void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
> -					u64 addr, u64 size)
> +u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
> +				      u64 addr, u64 size)
>  {
>  	struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
>  	pkvm_handle_t handle = kvm->arch.pkvm.handle;
>  
>  	if (!handle)
> -		return;
> +		return addr + size;
>  
>  	if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) {
>  		WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle));
> @@ -517,6 +517,8 @@ void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
>  		__pkvm_pgtable_stage2_reclaim(pgt, addr, addr + size);
>  	else
>  		__pkvm_pgtable_stage2_unshare(pgt, addr, addr + size);
> +
> +	return addr + size;
>  }
>  
>  void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
> -- 
> 2.47.3
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped
  2026-11-04 18:30   ` Oliver Upton
@ 2026-11-05  8:07     ` Fuad Tabba
  0 siblings, 0 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-11-05  8:07 UTC (permalink / raw)
  To: Oliver Upton
  Cc: Marc Zyngier, kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly,
	Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
	Will Deacon, Mark Rutland, Raghavendra Rao Ananta,
	Vincent Donnefort

On Wed, 4 Nov 2026 at 18:30, Oliver Upton <oupton@kernel.org> wrote:
>
> Hi Fuad,
>
> On Wed, Nov 04, 2026 at 12:44:50PM +0000, Fuad Tabba wrote:
> > +u64 kvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
> > +                                  u64 addr, u64 size)
>
> Returning a signed value here would be a good idea so you can percolate
> walk failures back up to the caller. Since that WARN_ON() below may as
> well be a BUG_ON() in hyp code.

Ack.

> Otherwise, rest looks good.

Thanks Oliver!
/fuad


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping
  2026-11-04 12:44 ` [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping Fuad Tabba
@ 2026-11-05  9:45   ` Vincent Donnefort
  2026-11-05 11:06     ` Fuad Tabba
  0 siblings, 1 reply; 10+ messages in thread
From: Vincent Donnefort @ 2026-11-05  9:45 UTC (permalink / raw)
  To: Fuad Tabba
  Cc: Marc Zyngier, Oliver Upton, kvmarm, linux-arm-kernel,
	linux-kernel, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland,
	Raghavendra Rao Ananta, Fuad Tabba

On Wed, Nov 04, 2026 at 12:44:51PM +0000, Fuad Tabba wrote:
> pKVM keeps a VM's stage-2 mappings in an interval tree, and
> pkvm_pgtable_stage2_destroy_range() releases the ones in its range.
> stage2_destroy_range() still calls it for every block-sized chunk of the
> IPA space. As pKVM gives every VM the host's IPA limit, a teardown with
> LPA2 makes 2^27 calls with 16K pages (2^22 with 4K), however little the
> VM maps.
> 
> Return the address of the next mapping instead, or U64_MAX when none is
> left, so that the teardown skips the space between mappings. No mapping
> crosses a chunk boundary, as pKVM maps at most PMD_SIZE, so the next one
> starts at or above the end of the range. A VM without a handle doesn't
> have anything mapped, so return U64_MAX for it straight away.
> 
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> ---
>  arch/arm64/kvm/pkvm.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index f21c3326dda68..77315c83c61b3 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -504,9 +504,10 @@ u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
>  {
>  	struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
>  	pkvm_handle_t handle = kvm->arch.pkvm.handle;
> +	struct pkvm_mapping *mapping;
>  
>  	if (!handle)
> -		return addr + size;
> +		return U64_MAX;
>  
>  	if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) {
>  		WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle));
> @@ -518,7 +519,10 @@ u64 pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt,
>  	else
>  		__pkvm_pgtable_stage2_unshare(pgt, addr, addr + size);
>  
> -	return addr + size;
> +	/* Mappings left by a failed release are still in the tree, so look past the range. */

Hum, not sure I understand that comment. Which release are we talking about? The
_reclaim or _unshare? If yes, we don't propagate the error and WARN if any of
them fails. So we don't expect any failure here and I'd say there's nothing
special to do?

Additionally, in any case, iter_first(addr + size) seems like the right thing to
do and is self-explanatory.

> +	mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr + size, U64_MAX);
> +
> +	return mapping ? gfn_to_gpa(mapping->gfn) : U64_MAX;
>  }
>  
>  void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
> -- 
> 2.47.3
> 

Reviewed-by: Vincent Donnefort <vdonnefort@google.com>

-- 
Vincent


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping
  2026-11-05  9:45   ` Vincent Donnefort
@ 2026-11-05 11:06     ` Fuad Tabba
  0 siblings, 0 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-11-05 11:06 UTC (permalink / raw)
  To: Vincent Donnefort
  Cc: Marc Zyngier, Oliver Upton, kvmarm, linux-arm-kernel,
	linux-kernel, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland,
	Raghavendra Rao Ananta

On Thu, 5 Nov 2026 at 09:45, Vincent Donnefort <vdonnefort@google.com> wrote:
[...]
> > -     return addr + size;
> > +     /* Mappings left by a failed release are still in the tree, so look past the range. */
>
> Hum, not sure I understand that comment. Which release are we talking about? The
> _reclaim or _unshare? If yes, we don't propagate the error and WARN if any of
> them fails. So we don't expect any failure here and I'd say there's nothing
> special to do?
>
> Additionally, in any case, iter_first(addr + size) seems like the right thing to
> do and is self-explanatory.

Yes, a leftover mapping would be returned again from addr. But since
that already WARNs, I'll drop the comment.

> > +     mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr + size, U64_MAX);
> > +
> > +     return mapping ? gfn_to_gpa(mapping->gfn) : U64_MAX;
> >  }
> >
> >  void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt)
> > --
> > 2.47.3
> >
>
> Reviewed-by: Vincent Donnefort <vdonnefort@google.com>

Thanks!
/fuad


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped
  2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
  2026-11-04 18:30   ` Oliver Upton
@ 2026-11-05 11:07   ` Suzuki K Poulose
  1 sibling, 0 replies; 10+ messages in thread
From: Suzuki K Poulose @ 2026-11-05 11:07 UTC (permalink / raw)
  To: Fuad Tabba, Marc Zyngier, Oliver Upton
  Cc: kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly, Steffen Eiden,
	Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland,
	Raghavendra Rao Ananta, Vincent Donnefort, Fuad Tabba

On 04/11/2026 12:44, Fuad Tabba wrote:
> Since commit 4ddfab5436b6 ("KVM: arm64: Reschedule as needed when
> destroying the stage-2 page-tables"), kvm_stage2_destroy() walks the
> IPA space one block-sized chunk at a time. The walker steps over an
> invalid entry in one go, but the next walk starts at the following
> chunk, so an invalid entry larger than a chunk is visited once for every
> chunk it covers. Destroying a VM with a 52-bit IPA then takes 2^27 walks
> with 16K pages (2^22 with 4K), however little it maps.
> 
> Have kvm_pgtable_stage2_destroy_range() return the address its walk
> reached, which kvm_pgtable_walk() doesn't expose, and resume from there.
> pkvm_pgtable_stage2_destroy_range() returns the end of its range, and
> EL2's kvm_pgtable_stage2_destroy() ignores the value.
> 
> Suggested-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Suggested-by: Oliver Upton <oupton@kernel.org>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>


Thanks Fuad for this, looks good to me with Oliver's suggestion.

Cheers
Suzuki



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size
  2026-11-04 12:44 [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Fuad Tabba
  2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
  2026-11-04 12:44 ` [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping Fuad Tabba
@ 2026-11-05 18:59 ` Wei-Lin Chang
  2026-11-05 19:48   ` Fuad Tabba
  2 siblings, 1 reply; 10+ messages in thread
From: Wei-Lin Chang @ 2026-11-05 18:59 UTC (permalink / raw)
  To: Fuad Tabba, Marc Zyngier, Oliver Upton
  Cc: kvmarm, linux-arm-kernel, linux-kernel, Joey Gouly, Steffen Eiden,
	Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
	Mark Rutland, Raghavendra Rao Ananta, Vincent Donnefort,
	Fuad Tabba

On Wed, Nov 04, 2026 at 12:44:49PM +0000, Fuad Tabba wrote:
> Hi folks,
> 
> Changes since v1 [1]:
>   - Run all teardown from stage2_destroy_range() (Oliver, Suzuki)
>   - Dropped the Fixes tags (Marc)
>   - Dropped the patch moving the teardown into kvm_vm_s2_ops
>   - Use gfn_to_gpa() for a mapping's address. (Vincent)
> 
> Two patches that make stage-2 teardown take time proportional to what
> is mapped, rather than to the size of the IPA space as it has since
> 4ddfab5436b6.
> 
> Under QEMU TCG ('-cpu max', 16K pages with LPA2), destroying even an
> empty VM takes about 7 seconds in pKVM. Without pKVM it takes about 19
> seconds if the VM requests a 52-bit IPA (crosvm requests the host's
> limit). With the series, both take well under a second. I tested only
> under QEMU, since I don't have hardware with LPA2.
> 
> The first has the page-table walk return where it stopped, so that the
> teardown steps over an invalid entry larger than a block-sized chunk in
> one go. The second has pKVM return where its next mapping starts.
> 
> pKVM gives every VM the host's IPA limit. My series honoring
> KVM_VM_TYPE_ARM_IPA_SIZE in pKVM [2] doesn't change that for crosvm,
> which requests the limit.
> 
> Based on Linux 7.4-rc1 (6628e7d4c6b25).
> 
> Cheers,
> /fuad
> 
> [1] https://lore.kernel.org/all/20261103084742.1302597-1-fuad.tabba@linux.dev/
> [2] https://lore.kernel.org/all/20260917091826.2639205-1-fuad.tabba@linux.dev/
> 

I haven't looked very closely, but do functions that use
stage2_apply_range() suffer a similar problem?

If I'm not mistaken we previously realized unmapping a full IPA with
kvm_vm_stage2_unmap_range() is slow partly for the same reason.

I'm not sure whether optimizing stage2_destroy_range() and
stage2_apply_range() together in a single series is preferrable though.

Thanks,
Wei-Lin Chang

> Fuad Tabba (2):
>   KVM: arm64: Resume stage-2 teardown where the walk stopped
>   KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping
> 
>  arch/arm64/include/asm/kvm_pgtable.h |  8 ++++++--
>  arch/arm64/include/asm/kvm_pkvm.h    |  4 ++--
>  arch/arm64/kvm/hyp/pgtable.c         | 15 ++++++++++++---
>  arch/arm64/kvm/mmu.c                 |  4 ++--
>  arch/arm64/kvm/pkvm.c                | 12 +++++++++---
>  5 files changed, 31 insertions(+), 12 deletions(-)
> 
> 
> base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
> -- 
> 2.47.3
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size
  2026-11-05 18:59 ` [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Wei-Lin Chang
@ 2026-11-05 19:48   ` Fuad Tabba
  0 siblings, 0 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-11-05 19:48 UTC (permalink / raw)
  To: Wei-Lin Chang
  Cc: Marc Zyngier, Oliver Upton, kvmarm, linux-arm-kernel,
	linux-kernel, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
	Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland,
	Raghavendra Rao Ananta, Vincent Donnefort

Hi Wei-Lin,

On Thu, 05 Nov 2026 18:59:55 +0000, Wei-Lin Chang <weilin.chang@arm.com> wrote:
[...]
> I haven't looked very closely, but do functions that use
> stage2_apply_range() suffer a similar problem?
>
> If I'm not mistaken we previously realized unmapping a full IPA with
> kvm_vm_stage2_unmap_range() is slow partly for the same reason.

Yes, when the range is the whole IPA, as you found on your reverse map
series [1].

> I'm not sure whether optimizing stage2_destroy_range() and
> stage2_apply_range() together in a single series is preferrable though.

I think it should be a separate series. Returning where the walk
stopped was a small change for teardown, which runs on an unlinked
table with no lock held. The stage2_apply_range() callbacks are shared
(kvm_pgtable_stage2_unmap() has EL2 callers that treat a non-zero
return as an error), and the walks drop the mmu_lock between chunks,
so it's a different change. Only NV passes stage2_apply_range() the
whole IPA.

Cheers,
/fuad

[1] https://lore.kernel.org/all/eylfcgxs2xzu57czyuhkpmum45lpxyccyu2yh3mipr67aq3xo6@xinygxzavoja/


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-11-05 19:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04 12:44 [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Fuad Tabba
2026-11-04 12:44 ` [PATCH v2 1/2] KVM: arm64: Resume stage-2 teardown where the walk stopped Fuad Tabba
2026-11-04 18:30   ` Oliver Upton
2026-11-05  8:07     ` Fuad Tabba
2026-11-05 11:07   ` Suzuki K Poulose
2026-11-04 12:44 ` [PATCH v2 2/2] KVM: arm64: Resume a pKVM stage-2 teardown at the next mapping Fuad Tabba
2026-11-05  9:45   ` Vincent Donnefort
2026-11-05 11:06     ` Fuad Tabba
2026-11-05 18:59 ` [PATCH v2 0/2] KVM: arm64: Stop stage-2 teardown scaling with the IPA size Wei-Lin Chang
2026-11-05 19:48   ` Fuad Tabba

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox