KVM: arm64: nv: Clarify safety of allowing TLBI unmaps to reschedule
authorOliver Upton <oliver.upton@linux.dev>
Mon, 7 Oct 2024 23:30:28 +0000 (23:30 +0000)
committerMarc Zyngier <maz@kernel.org>
Tue, 8 Oct 2024 09:40:27 +0000 (10:40 +0100)
There's been a decent amount of attention around unmaps of nested MMUs,
and TLBI handling is no exception to this. Add a comment clarifying why
it is safe to reschedule during a TLBI unmap, even without a reference
on the MMU in progress.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20241007233028.2236133-5-oliver.upton@linux.dev
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/sys_regs.c

index edd385baf270069ce00e6c58c031787025005d4a..1d84bc3396cbc658ed0ea65da91ba8013c01467f 100644 (file)
@@ -2989,6 +2989,29 @@ union tlbi_info {
 static void s2_mmu_unmap_range(struct kvm_s2_mmu *mmu,
                               const union tlbi_info *info)
 {
+       /*
+        * The unmap operation is allowed to drop the MMU lock and block, which
+        * means that @mmu could be used for a different context than the one
+        * currently being invalidated.
+        *
+        * This behavior is still safe, as:
+        *
+        *  1) The vCPU(s) that recycled the MMU are responsible for invalidating
+        *     the entire MMU before reusing it, which still honors the intent
+        *     of a TLBI.
+        *
+        *  2) Until the guest TLBI instruction is 'retired' (i.e. increment PC
+        *     and ERET to the guest), other vCPUs are allowed to use stale
+        *     translations.
+        *
+        *  3) Accidentally unmapping an unrelated MMU context is nonfatal, and
+        *     at worst may cause more aborts for shadow stage-2 fills.
+        *
+        * Dropping the MMU lock also implies that shadow stage-2 fills could
+        * happen behind the back of the TLBI. This is still safe, though, as
+        * the L1 needs to put its stage-2 in a consistent state before doing
+        * the TLBI.
+        */
        kvm_stage2_unmap_range(mmu, info->range.start, info->range.size, true);
 }
 
@@ -3084,6 +3107,10 @@ static void s2_mmu_unmap_ipa(struct kvm_s2_mmu *mmu,
        max_size = compute_tlb_inval_range(mmu, info->ipa.addr);
        base_addr &= ~(max_size - 1);
 
+       /*
+        * See comment in s2_mmu_unmap_range() for why this is allowed to
+        * reschedule.
+        */
        kvm_stage2_unmap_range(mmu, base_addr, max_size, true);
 }