diff mbox series

arm64: context: Free up kernel ASIDs if KPTI is not in use

Message ID 1578392883-13294-1-git-send-email-vladimir.murzin@arm.com
State Mainlined
Commit f88f42f853a80d9b087f0c2035d6fbab504ea54c
Headers show
Series arm64: context: Free up kernel ASIDs if KPTI is not in use | expand

Commit Message

Vladimir Murzin Jan. 7, 2020, 10:28 a.m. UTC
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(-)

Comments

Catalin Marinas Jan. 9, 2020, 12:08 p.m. UTC | #1
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 mbox series

Patch

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;
 }