Skip to content

Commit 809b2e6

Browse files
Marc Zyngieroupton
authored andcommitted
KVM: arm64: nv: Invalidate TLBs based on shadow S2 TTL-like information
In order to be able to make S2 TLB invalidations more performant on NV, let's use a scheme derived from the FEAT_TTL extension. If bits [56:55] in the leaf descriptor translating the address in the corresponding shadow S2 are non-zero, they indicate a level which can be used as an invalidation range. This allows further reduction of the systematic over-invalidation that takes place otherwise. Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent b1a3a94 commit 809b2e6

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

arch/arm64/kvm/nested.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Author: Jintack Lim <[email protected]>
55
*/
66

7+
#include <linux/bitfield.h>
78
#include <linux/kvm.h>
89
#include <linux/kvm_host.h>
910

@@ -420,12 +421,94 @@ static unsigned int ttl_to_size(u8 ttl)
420421
return max_size;
421422
}
422423

424+
/*
425+
* Compute the equivalent of the TTL field by parsing the shadow PT. The
426+
* granule size is extracted from the cached VTCR_EL2.TG0 while the level is
427+
* retrieved from first entry carrying the level as a tag.
428+
*/
429+
static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)
430+
{
431+
u64 tmp, sz = 0, vtcr = mmu->tlb_vtcr;
432+
kvm_pte_t pte;
433+
u8 ttl, level;
434+
435+
lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);
436+
437+
switch (vtcr & VTCR_EL2_TG0_MASK) {
438+
case VTCR_EL2_TG0_4K:
439+
ttl = (TLBI_TTL_TG_4K << 2);
440+
break;
441+
case VTCR_EL2_TG0_16K:
442+
ttl = (TLBI_TTL_TG_16K << 2);
443+
break;
444+
case VTCR_EL2_TG0_64K:
445+
default: /* IMPDEF: treat any other value as 64k */
446+
ttl = (TLBI_TTL_TG_64K << 2);
447+
break;
448+
}
449+
450+
tmp = addr;
451+
452+
again:
453+
/* Iteratively compute the block sizes for a particular granule size */
454+
switch (vtcr & VTCR_EL2_TG0_MASK) {
455+
case VTCR_EL2_TG0_4K:
456+
if (sz < SZ_4K) sz = SZ_4K;
457+
else if (sz < SZ_2M) sz = SZ_2M;
458+
else if (sz < SZ_1G) sz = SZ_1G;
459+
else sz = 0;
460+
break;
461+
case VTCR_EL2_TG0_16K:
462+
if (sz < SZ_16K) sz = SZ_16K;
463+
else if (sz < SZ_32M) sz = SZ_32M;
464+
else sz = 0;
465+
break;
466+
case VTCR_EL2_TG0_64K:
467+
default: /* IMPDEF: treat any other value as 64k */
468+
if (sz < SZ_64K) sz = SZ_64K;
469+
else if (sz < SZ_512M) sz = SZ_512M;
470+
else sz = 0;
471+
break;
472+
}
473+
474+
if (sz == 0)
475+
return 0;
476+
477+
tmp &= ~(sz - 1);
478+
if (kvm_pgtable_get_leaf(mmu->pgt, tmp, &pte, NULL))
479+
goto again;
480+
if (!(pte & PTE_VALID))
481+
goto again;
482+
level = FIELD_GET(KVM_NV_GUEST_MAP_SZ, pte);
483+
if (!level)
484+
goto again;
485+
486+
ttl |= level;
487+
488+
/*
489+
* We now have found some level information in the shadow S2. Check
490+
* that the resulting range is actually including the original IPA.
491+
*/
492+
sz = ttl_to_size(ttl);
493+
if (addr < (tmp + sz))
494+
return ttl;
495+
496+
return 0;
497+
}
498+
423499
unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)
424500
{
501+
struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
425502
unsigned long max_size;
426503
u8 ttl;
427504

428-
ttl = FIELD_GET(GENMASK_ULL(47, 44), val);
505+
ttl = FIELD_GET(TLBI_TTL_MASK, val);
506+
507+
if (!ttl || !kvm_has_feat(kvm, ID_AA64MMFR2_EL1, TTL, IMP)) {
508+
/* No TTL, check the shadow S2 for a hint */
509+
u64 addr = (val & GENMASK_ULL(35, 0)) << 12;
510+
ttl = get_guest_mapping_ttl(mmu, addr);
511+
}
429512

430513
max_size = ttl_to_size(ttl);
431514

0 commit comments

Comments
 (0)