Message ID | 1578392883-13294-1-git-send-email-vladimir.murzin@arm.com (mailing list archive) |
---|---|
State | Mainlined |
Commit | f88f42f853a80d9b087f0c2035d6fbab504ea54c |
Headers | show |
Series | arm64: context: Free up kernel ASIDs if KPTI is not in use | expand |
On Tue, Jan 07, 2020 at 10:28:03AM +0000, Vladimir Murzin wrote: > We can extend user ASID space if it turns out that system does not > require KPTI. We start with kernel ASIDs reserved because CPU caps are > not finalized yet and free them up lazily on the next rollover if we > confirm than KPTI is not in use. > > Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com> > --- > arch/arm64/mm/context.c | 38 ++++++++++++++++++++++++++++++-------- > 1 file changed, 30 insertions(+), 8 deletions(-) > > diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c > index b5e329f..8ef73e8 100644 > --- a/arch/arm64/mm/context.c > +++ b/arch/arm64/mm/context.c > @@ -29,15 +29,9 @@ static cpumask_t tlb_flush_pending; > #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) > #define ASID_FIRST_VERSION (1UL << asid_bits) > > -#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 > -#define NUM_USER_ASIDS (ASID_FIRST_VERSION >> 1) > -#define asid2idx(asid) (((asid) & ~ASID_MASK) >> 1) > -#define idx2asid(idx) (((idx) << 1) & ~ASID_MASK) > -#else > -#define NUM_USER_ASIDS (ASID_FIRST_VERSION) > +#define NUM_USER_ASIDS ASID_FIRST_VERSION > #define asid2idx(asid) ((asid) & ~ASID_MASK) > #define idx2asid(idx) asid2idx(idx) > -#endif > > /* Get the ASIDBits supported by the current CPU */ > static u32 get_cpu_asid_bits(void) > @@ -77,13 +71,33 @@ void verify_cpu_asid_bits(void) > } > } > > +static void set_kpti_asid_bits(void) > +{ > + unsigned int len = BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(unsigned long); > + /* > + * In case of KPTI kernel/user ASIDs are allocated in > + * pairs, the bottom bit distinguishes the two: if it > + * is set, then the ASID will map only userspace. Thus > + * mark even as reserved for kernel. > + */ > + memset(asid_map, 0xaa, len); > +} > + > +static void set_reserved_asid_bits(void) > +{ > + if (arm64_kernel_unmapped_at_el0()) > + set_kpti_asid_bits(); > + else > + bitmap_clear(asid_map, 0, NUM_USER_ASIDS); > +} > + > static void flush_context(void) > { > int i; > u64 asid; > > /* Update the list of reserved ASIDs and the ASID bitmap. */ > - bitmap_clear(asid_map, 0, NUM_USER_ASIDS); > + set_reserved_asid_bits(); > > for_each_possible_cpu(i) { > asid = atomic64_xchg_relaxed(&per_cpu(active_asids, i), 0); > @@ -261,6 +275,14 @@ static int asids_init(void) > panic("Failed to allocate bitmap for %lu ASIDs\n", > NUM_USER_ASIDS); > > + /* > + * We cannot call set_reserved_asid_bits() here because CPU > + * caps are not finalized yet, so it is safer to assume KPTI > + * and reserve kernel ASID's from beginning. > + */ > + if (IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) > + set_kpti_asid_bits(); > + > pr_info("ASID allocator initialised with %lu entries\n", NUM_USER_ASIDS); > return 0; > } Even if we don't need KPTI, we still reserve half of ASIDs until the first roll-over but that's fine. I was hoping we can get rid of IS_ENABLED() and call set_reserved_asid_bits() directly in asids_init() once the patch below is merged: https://lore.kernel.org/linux-arm-kernel/20191209181217.44890-5-broonie@kernel.org/ but we can still turn KPTI on during boot even if the primary CPU didn't have it. Also deferring asids_init() may not interact well with EFI run-time services which need an ASID. Anyway, your patch looks fine to me: Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index b5e329f..8ef73e8 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -29,15 +29,9 @@ static cpumask_t tlb_flush_pending; #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) #define ASID_FIRST_VERSION (1UL << asid_bits) -#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 -#define NUM_USER_ASIDS (ASID_FIRST_VERSION >> 1) -#define asid2idx(asid) (((asid) & ~ASID_MASK) >> 1) -#define idx2asid(idx) (((idx) << 1) & ~ASID_MASK) -#else -#define NUM_USER_ASIDS (ASID_FIRST_VERSION) +#define NUM_USER_ASIDS ASID_FIRST_VERSION #define asid2idx(asid) ((asid) & ~ASID_MASK) #define idx2asid(idx) asid2idx(idx) -#endif /* Get the ASIDBits supported by the current CPU */ static u32 get_cpu_asid_bits(void) @@ -77,13 +71,33 @@ void verify_cpu_asid_bits(void) } } +static void set_kpti_asid_bits(void) +{ + unsigned int len = BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(unsigned long); + /* + * In case of KPTI kernel/user ASIDs are allocated in + * pairs, the bottom bit distinguishes the two: if it + * is set, then the ASID will map only userspace. Thus + * mark even as reserved for kernel. + */ + memset(asid_map, 0xaa, len); +} + +static void set_reserved_asid_bits(void) +{ + if (arm64_kernel_unmapped_at_el0()) + set_kpti_asid_bits(); + else + bitmap_clear(asid_map, 0, NUM_USER_ASIDS); +} + static void flush_context(void) { int i; u64 asid; /* Update the list of reserved ASIDs and the ASID bitmap. */ - bitmap_clear(asid_map, 0, NUM_USER_ASIDS); + set_reserved_asid_bits(); for_each_possible_cpu(i) { asid = atomic64_xchg_relaxed(&per_cpu(active_asids, i), 0); @@ -261,6 +275,14 @@ static int asids_init(void) panic("Failed to allocate bitmap for %lu ASIDs\n", NUM_USER_ASIDS); + /* + * We cannot call set_reserved_asid_bits() here because CPU + * caps are not finalized yet, so it is safer to assume KPTI + * and reserve kernel ASID's from beginning. + */ + if (IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0)) + set_kpti_asid_bits(); + pr_info("ASID allocator initialised with %lu entries\n", NUM_USER_ASIDS); return 0; }
We can extend user ASID space if it turns out that system does not require KPTI. We start with kernel ASIDs reserved because CPU caps are not finalized yet and free them up lazily on the next rollover if we confirm than KPTI is not in use. Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com> --- arch/arm64/mm/context.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-)