Thread (14 messages) 14 messages, 3 authors, 2023-01-25

Re: [RFC PATCH 5/6] KVM: arm64: Optimize the stage2 map path with TLBI range instructions

From: Raghavendra Rao Ananta <hidden>
Date: 2023-01-25 22:20:55
Also in: kvm, kvmarm, lkml

Hi Oliver,

On Tue, Jan 24, 2023 at 3:25 PM Oliver Upton [off-list ref] wrote:
Hi Raghavendra,

My comment from the previous change also applies here:

  KVM: arm64: Use range-based TLBIs when collapsing hugepages

On Mon, Jan 09, 2023 at 09:53:46PM +0000, Raghavendra Rao Ananta wrote:
quoted
Currently, when the map path of stage2 page-table coalesces a
bunch of pages into a hugepage, KVM invalidates the entire
VM's TLB entries. This would cause a perforamance penality for
the guest whose pages have already been coalesced earlier as they
would have to refill their TLB entries unnecessarily again.
It is also problematic that we do this on every single fault where we
collapse a hugepage.
Yes! I'll also include this description in v2.
quoted
Hence, if the system supports it, use __kvm_tlb_flush_range_vmid_ipa()
to flush only the range of pages that have been combined into
a hugepage, while leaving other TLB entries alone.

Signed-off-by: Raghavendra Rao Ananta <redacted>
---
 arch/arm64/kvm/hyp/pgtable.c | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c
index b11cf2c618a6c..099032bb01bce 100644
--- a/arch/arm64/kvm/hyp/pgtable.c
+++ b/arch/arm64/kvm/hyp/pgtable.c
@@ -686,6 +686,22 @@ static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_
      return cmpxchg(ctx->ptep, ctx->old, new) == ctx->old;
 }

+static void kvm_table_pte_flush(struct kvm_s2_mmu *mmu, u64 addr, u32 level, u32 tlb_level)
Could you call this something like kvm_pgtable_flush_range() and take an
address range as an argument? TLBIRANGE can be used outside the context
of a table (i.e. a subset of PTEs).
Good idea. In that case, the function becomes very close to arm64's
implementation of kvm_flush_remote_tlbs_range() on top of David's
series [1].
Too bad, we may not be able to invoke that. I'll rename the function.
quoted
+{
+     if (system_supports_tlb_range()) {
+             u64 end = addr + kvm_granule_size(level);
+
+             kvm_call_hyp(__kvm_tlb_flush_range_vmid_ipa, mmu, addr, end, tlb_level);
+     } else {
+             /*
+              * Invalidate the whole stage-2, as we may have numerous leaf
+              * entries below us which would otherwise need invalidating
+              * individually.
+              */
+             kvm_call_hyp(__kvm_tlb_flush_vmid, mmu);
+     }
+}
+
 /**
  * stage2_try_break_pte() - Invalidates a pte according to the
  *                       'break-before-make' requirements of the
@@ -693,6 +709,7 @@ static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_
  *
  * @ctx: context of the visited pte.
  * @mmu: stage-2 mmu
+ * @tlb_level: The level at which the leaf pages are expected (for FEAT_TTL hint)
Do we need the caller to provide the TTL hint? We already have
ctx->level, and stage2_try_break_pte() also knows what the removed PTE
contained (i.e. a table or a block/page).
ctx->level may not always translate to TTL level hint. For example,
the patch 6/6 of this series also calls stage2_try_break_pte(), but
from the very top level (level-1). In that case, the level can be
extracted from ctx, but we won't have any idea what the TTL hint is
since we won't be traversing all of the page-table. As a result, we
pass 0. However, if we are remapping a table to a bunch of pages, TTL
level hint could be just one level down.
quoted
  * Returns: true if the pte was successfully broken.
  *
@@ -701,7 +718,7 @@ static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_
  * on the containing table page.
  */
 static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
-                              struct kvm_s2_mmu *mmu)
+                              struct kvm_s2_mmu *mmu, u32 tlb_level)
 {
      struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops;
@@ -722,7 +739,7 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx,
       * value (if any).
       */
      if (kvm_pte_table(ctx->old, ctx->level))
-             kvm_call_hyp(__kvm_tlb_flush_vmid, mmu);
+             kvm_table_pte_flush(mmu, ctx->addr, ctx->level, tlb_level);
I don't think we should provide a TTL hint for a removed table. It is
entirely possible for the unlinked table to contain a mix of blocks and
pages, meaning there isn't a uniform table level for the whole range.
True, but it's particularly useful for the case where we know that a
table is going to be replaced by a block. In that case, can't we be
sure of the TTL level? Perhaps for other cases, we can supply 0. WDYT?
quoted
      else if (kvm_pte_valid(ctx->old))
              kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, ctx->addr, ctx->level);
@@ -804,7 +821,7 @@ static int stage2_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx,
      if (!stage2_pte_needs_update(ctx->old, new))
              return -EAGAIN;

-     if (!stage2_try_break_pte(ctx, data->mmu))
+     if (!stage2_try_break_pte(ctx, data->mmu, ctx->level))
              return -EAGAIN;

      /* Perform CMOs before installation of the guest stage-2 PTE */
@@ -861,7 +878,11 @@ static int stage2_map_walk_leaf(const struct kvm_pgtable_visit_ctx *ctx,
      if (!childp)
              return -ENOMEM;

-     if (!stage2_try_break_pte(ctx, data->mmu)) {
+     /*
+      * As the table will be replaced with a block, one level down would
+      * be the current page entries held by the table.
+      */
This isn't necessarily true. Ignoring mixed block/pages for a moment,
Collapsing a PUD entry into a block after dirty logging (where we mapped
at PTE level) would imply a TTL of ctx->level + 2.

But again, I think it is best to provide no hint in this case.
Ah, right. We could also collapse into a 1G block. No hint would be
better in that case.

Thanks,
Raghavendra

[1]: https://lore.kernel.org/linux-arm-kernel/20230119173559.2517103-1-dmatlack@google.com/T/#meca775fa8accc996900d5e9caeb004f7b9774627 (local)
--
Thanks,
Oliver
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help