Skip to contentSkip to conversation

arm64 switch_mm improvements

PATCH New Will Deacon 4 participants 15 messages Oct 6, 2015 – Oct 7, 2015

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v2 cover arm64 switch_mm improvements

Hi all,

This is version two of the patches previously posted here:

http://lists.infradead.org/pipermail/linux-arm-kernel/2015-September/370720.html

Changes since v1 include:

* More comments
* Added reviewed-by tags

Cheers,

Will

--->8

Will Deacon (10):
arm64: mm: remove unused cpu_set_idmap_tcr_t0sz function
arm64: proc: de-scope TLBI operation during cold boot
arm64: flush: use local TLB and I-cache invalidation
arm64: mm: rewrite ASID allocator and MM context-switching code
arm64: tlbflush: remove redundant ASID casts to (unsigned long)
arm64: tlbflush: avoid flushing when fullmm == 1
arm64: switch_mm: simplify mm and CPU checks
arm64: mm: kill mm_cpumask usage
arm64: tlb: remove redundant barrier from __flush_tlb_pgtable
arm64: mm: remove dsb from update_mmu_cache

 arch/arm64/include/asm/cacheflush.h  |   7 ++
 arch/arm64/include/asm/mmu.h         |  15 +--
 arch/arm64/include/asm/mmu_context.h | 113 ++++-------------
 arch/arm64/include/asm/pgtable.h     |   6 +-
 arch/arm64/include/asm/thread_info.h |   1 -
 arch/arm64/include/asm/tlb.h         |  26 ++--
 arch/arm64/include/asm/tlbflush.h    |  18 ++-
 arch/arm64/kernel/asm-offsets.c      |   2 +-
 arch/arm64/kernel/efi.c              |   5 +-
 arch/arm64/kernel/smp.c              |   9 +-
 arch/arm64/kernel/suspend.c          |   2 +-
 arch/arm64/mm/context.c              | 236 +++++++++++++++++++++--------------
 arch/arm64/mm/mmu.c                  |   2 +-
 arch/arm64/mm/proc.S                 |   6 +-
 14 files changed, 222 insertions(+), 226 deletions(-)
Signature · 1 line
2.1.4
#1 lore raw

PATCH v2 1/10 arm64: mm: remove unused cpu_set_idmap_tcr_t0sz function

With commit b08d4640a3dc ("arm64: remove dead code"), cpu_set_idmap_tcr_t0sz is no longer called and can therefore be removed from the kernel.

This patch removes the function and effectively inlines the helper function __cpu_set_tcr_t0sz into cpu_set_default_tcr_t0sz.

---
 arch/arm64/include/asm/mmu_context.h | 35 ++++++++++++-----------------------
 1 file changed, 12 insertions(+), 23 deletions(-)
arch/arm64/include/asm/mmu_context.h +12 -23
index 8ec41e5f56f0..549b89554ce8 100644
@@ -77,34 +77,23 @@ static inline bool __cpu_uses_extended_idmap(void)
 		unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS)));
 }
 
-static inline void __cpu_set_tcr_t0sz(u64 t0sz)
-{
-	unsigned long tcr;
-
-	if (__cpu_uses_extended_idmap())
-		asm volatile (
-		"	mrs	%0, tcr_el1	;"
-		"	bfi	%0, %1, %2, %3	;"
-		"	msr	tcr_el1, %0	;"
-		"	isb"
-		: "=&r" (tcr)
-		: "r"(t0sz), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
-}
-
-/*
- * Set TCR.T0SZ to the value appropriate for activating the identity map.
- */
-static inline void cpu_set_idmap_tcr_t0sz(void)
-{
-	__cpu_set_tcr_t0sz(idmap_t0sz);
-}
-
 /*
  * Set TCR.T0SZ to its default value (based on VA_BITS)
  */
 static inline void cpu_set_default_tcr_t0sz(void)
 {
-	__cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS));
+	unsigned long tcr;
+
+	if (!__cpu_uses_extended_idmap())
+		return;
+
+	asm volatile (
+	"	mrs	%0, tcr_el1	;"
+	"	bfi	%0, %1, %2, %3	;"
+	"	msr	tcr_el1, %0	;"
+	"	isb"
+	: "=&r" (tcr)
+	: "r"(TCR_T0SZ(VA_BITS)), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
 }
 
 static inline void switch_new_context(struct mm_struct *mm)
Signature · 1 line
2.1.4
#2 lore raw

PATCH v2 1/10 arm64: mm: remove unused cpu_set_idmap_tcr_t0sz function

Quoted 9 lines · Will Deacon

With commit b08d4640a3dc ("arm64: remove dead code"), cpu_set_idmap_tcr_t0sz is no longer called and can therefore be removed from the kernel.

This patch removes the function and effectively inlines the helper function __cpu_set_tcr_t0sz into cpu_set_default_tcr_t0sz.

Quoted 63 lines
---
 arch/arm64/include/asm/mmu_context.h | 35 ++++++++++++-----------------------
 1 file changed, 12 insertions(+), 23 deletions(-)
arch/arm64/include/asm/mmu_context.h +12 -24
index 8ec41e5f56f0..549b89554ce8 100644
@@ -77,34 +77,23 @@ static inline bool __cpu_uses_extended_idmap(void)
                unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS)));
 }
 
-static inline void __cpu_set_tcr_t0sz(u64 t0sz)
-{
-       unsigned long tcr;
-
-       if (__cpu_uses_extended_idmap())
-               asm volatile (
-               "       mrs     %0, tcr_el1     ;"
-               "       bfi     %0, %1, %2, %3  ;"
-               "       msr     tcr_el1, %0     ;"
-               "       isb"
-               : "=&r" (tcr)
-               : "r"(t0sz), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
-}
-
-/*
- * Set TCR.T0SZ to the value appropriate for activating the identity map.
- */
-static inline void cpu_set_idmap_tcr_t0sz(void)
-{
-       __cpu_set_tcr_t0sz(idmap_t0sz);
-}
-
 /*
  * Set TCR.T0SZ to its default value (based on VA_BITS)
  */
 static inline void cpu_set_default_tcr_t0sz(void)
 {
-       __cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS));
+       unsigned long tcr;
+
+       if (!__cpu_uses_extended_idmap())
+               return;
+
+       asm volatile (
+       "       mrs     %0, tcr_el1     ;"
+       "       bfi     %0, %1, %2, %3  ;"
+       "       msr     tcr_el1, %0     ;"
+       "       isb"
+       : "=&r" (tcr)
+       : "r"(TCR_T0SZ(VA_BITS)), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
 }
 
 static inline void switch_new_context(struct mm_struct *mm)
--

2.1.4

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#3 lore raw

PATCH v2 2/10 arm64: proc: de-scope TLBI operation during cold boot

In reply to Will Deacon

When cold-booting a CPU, we must invalidate any junk entries from the local TLB prior to enabling the MMU. This doesn't require broadcasting within the inner-shareable domain, so de-scope the operation to apply only to the local CPU.

---
 arch/arm64/mm/proc.S | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/mm/proc.S +2 -2
index e4ee7bd8830a..bbde13d77da5 100644
@@ -146,8 +146,8 @@ ENDPROC(cpu_do_switch_mm)
  *	value of the SCTLR_EL1 register.
  */
 ENTRY(__cpu_setup)
-	tlbi	vmalle1is			// invalidate I + D TLBs
-	dsb	ish
+	tlbi	vmalle1				// Invalidate local TLB
+	dsb	nsh
 
 	mov	x0, #3 << 20
 	msr	cpacr_el1, x0			// Enable FP/ASIMD
Signature · 1 line
2.1.4
#4 lore raw

PATCH v2 3/10 arm64: flush: use local TLB and I-cache invalidation

In reply to Will Deacon

There are a number of places where a single CPU is running with a private page-table and we need to perform maintenance on the TLB and I-cache in order to ensure correctness, but do not require the operation to be broadcast to other CPUs.

This patch adds local variants of tlb_flush_all and __flush_icache_all to support these use-cases and updates the callers respectively. __local_flush_icache_all also implies an isb, since it is intended to be used synchronously.

---
 arch/arm64/include/asm/cacheflush.h | 7 +++++++
 arch/arm64/include/asm/tlbflush.h   | 8 ++++++++
 arch/arm64/kernel/efi.c             | 4 ++--
 arch/arm64/kernel/smp.c             | 2 +-
 arch/arm64/kernel/suspend.c         | 2 +-
 arch/arm64/mm/context.c             | 4 ++--
 arch/arm64/mm/mmu.c                 | 2 +-
 7 files changed, 22 insertions(+), 7 deletions(-)
arch/arm64/include/asm/cacheflush.h +7 -0
index c75b8d027eb1..54efedaf331f 100644
@@ -115,6 +115,13 @@ extern void copy_to_user_page(struct vm_area_struct *, struct page *,
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
 extern void flush_dcache_page(struct page *);
 
+static inline void __local_flush_icache_all(void)
+{
+	asm("ic iallu");
+	dsb(nsh);
+	isb();
+}
+
 static inline void __flush_icache_all(void)
 {
 	asm("ic	ialluis");
arch/arm64/include/asm/tlbflush.h +8 -0
index 7bd2da021658..96f944e75dc4 100644
@@ -63,6 +63,14 @@
  *		only require the D-TLB to be invalidated.
  *		- kaddr - Kernel virtual memory address
  */
+static inline void local_flush_tlb_all(void)
+{
+	dsb(nshst);
+	asm("tlbi	vmalle1");
+	dsb(nsh);
+	isb();
+}
+
 static inline void flush_tlb_all(void)
 {
 	dsb(ishst);
arch/arm64/kernel/efi.c +2 -2
index 13671a9cf016..4d12926ea40d 100644
@@ -344,9 +344,9 @@ static void efi_set_pgd(struct mm_struct *mm)
 	else
 		cpu_switch_mm(mm->pgd, mm);
 
-	flush_tlb_all();
+	local_flush_tlb_all();
 	if (icache_is_aivivt())
-		__flush_icache_all();
+		__local_flush_icache_all();
 }
 
 void efi_virtmap_load(void)
arch/arm64/kernel/smp.c +1 -1
index dbdaacddd9a5..fdd4d4dbd64f 100644
@@ -152,7 +152,7 @@ asmlinkage void secondary_start_kernel(void)
 	 * point to zero page to avoid speculatively fetching new entries.
 	 */
 	cpu_set_reserved_ttbr0();
-	flush_tlb_all();
+	local_flush_tlb_all();
 	cpu_set_default_tcr_t0sz();
 
 	preempt_disable();
arch/arm64/kernel/suspend.c +1 -1
index 8297d502217e..3c5e4e6dcf68 100644
@@ -90,7 +90,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
 		else
 			cpu_switch_mm(mm->pgd, mm);
 
-		flush_tlb_all();
+		local_flush_tlb_all();
 
 		/*
 		 * Restore per-cpu offset before any kernel
arch/arm64/mm/context.c +2 -2
index d70ff14dbdbd..48b53fb381af 100644
@@ -48,9 +48,9 @@ static void flush_context(void)
 {
 	/* set the reserved TTBR0 before flushing the TLB */
 	cpu_set_reserved_ttbr0();
-	flush_tlb_all();
+	local_flush_tlb_all();
 	if (icache_is_aivivt())
-		__flush_icache_all();
+		__local_flush_icache_all();
 }
 
 static void set_mm_context(struct mm_struct *mm, unsigned int asid)
arch/arm64/mm/mmu.c +1 -1
index 9211b8527f25..71a310478c9e 100644
@@ -456,7 +456,7 @@ void __init paging_init(void)
 	 * point to zero page to avoid speculatively fetching new entries.
 	 */
 	cpu_set_reserved_ttbr0();
-	flush_tlb_all();
+	local_flush_tlb_all();
 	cpu_set_default_tcr_t0sz();
 }
 
Signature · 1 line
2.1.4
#5 lore raw

PATCH v2 3/10 arm64: flush: use local TLB and I-cache invalidation

Quoted 5 lines · Will Deacon

There are a number of places where a single CPU is running with a private page-table and we need to perform maintenance on the TLB and I-cache in order to ensure correctness, but do not require the operation to be broadcast to other CPUs.

Quoted 7 lines

This patch adds local variants of tlb_flush_all and __flush_icache_all to support these use-cases and updates the callers respectively. __local_flush_icache_all also implies an isb, since it is intended to be used synchronously.

I like this one. It is similar to what some of my earlier patches did.

Quoted 10 lines
---
  arch/arm64/include/asm/cacheflush.h | 7 +++++++
  arch/arm64/include/asm/tlbflush.h   | 8 ++++++++
  arch/arm64/kernel/efi.c             | 4 ++--
  arch/arm64/kernel/smp.c             | 2 +-
  arch/arm64/kernel/suspend.c         | 2 +-
  arch/arm64/mm/context.c             | 4 ++--
  arch/arm64/mm/mmu.c                 | 2 +-
  7 files changed, 22 insertions(+), 7 deletions(-)

[...]

#6 lore raw

PATCH v2 3/10 arm64: flush: use local TLB and I-cache invalidation

In reply to Will Deacon
Quoted 5 lines · Will Deacon

There are a number of places where a single CPU is running with a private page-table and we need to perform maintenance on the TLB and I-cache in order to ensure correctness, but do not require the operation to be broadcast to other CPUs.

Quoted 7 lines

This patch adds local variants of tlb_flush_all and __flush_icache_all to support these use-cases and updates the callers respectively. __local_flush_icache_all also implies an isb, since it is intended to be used synchronously.

Quoted 126 lines
---
 arch/arm64/include/asm/cacheflush.h | 7 +++++++
 arch/arm64/include/asm/tlbflush.h   | 8 ++++++++
 arch/arm64/kernel/efi.c             | 4 ++--
 arch/arm64/kernel/smp.c             | 2 +-
 arch/arm64/kernel/suspend.c         | 2 +-
 arch/arm64/mm/context.c             | 4 ++--
 arch/arm64/mm/mmu.c                 | 2 +-
 7 files changed, 22 insertions(+), 7 deletions(-)
arch/arm64/include/asm/cacheflush.h +7 -0
index c75b8d027eb1..54efedaf331f 100644
@@ -115,6 +115,13 @@ extern void copy_to_user_page(struct vm_area_struct *, struct page *,
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
 extern void flush_dcache_page(struct page *);
 
+static inline void __local_flush_icache_all(void)
+{
+       asm("ic iallu");
+       dsb(nsh);
+       isb();
+}
+
 static inline void __flush_icache_all(void)
 {
        asm("ic ialluis");
arch/arm64/include/asm/tlbflush.h +8 -0
index 7bd2da021658..96f944e75dc4 100644
@@ -63,6 +63,14 @@
  *             only require the D-TLB to be invalidated.
  *             - kaddr - Kernel virtual memory address
  */
+static inline void local_flush_tlb_all(void)
+{
+       dsb(nshst);
+       asm("tlbi       vmalle1");
+       dsb(nsh);
+       isb();
+}
+
 static inline void flush_tlb_all(void)
 {
        dsb(ishst);
arch/arm64/kernel/efi.c +2 -2
index 13671a9cf016..4d12926ea40d 100644
@@ -344,9 +344,9 @@ static void efi_set_pgd(struct mm_struct *mm)
        else
                cpu_switch_mm(mm->pgd, mm);
 
-       flush_tlb_all();
+       local_flush_tlb_all();
        if (icache_is_aivivt())
-               __flush_icache_all();
+               __local_flush_icache_all();
 }
 
 void efi_virtmap_load(void)
arch/arm64/kernel/smp.c +1 -1
index dbdaacddd9a5..fdd4d4dbd64f 100644
@@ -152,7 +152,7 @@ asmlinkage void secondary_start_kernel(void)
         * point to zero page to avoid speculatively fetching new entries.
         */
        cpu_set_reserved_ttbr0();
-       flush_tlb_all();
+       local_flush_tlb_all();
        cpu_set_default_tcr_t0sz();
 
        preempt_disable();
arch/arm64/kernel/suspend.c +1 -1
index 8297d502217e..3c5e4e6dcf68 100644
@@ -90,7 +90,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
                else
                        cpu_switch_mm(mm->pgd, mm);
 
-               flush_tlb_all();
+               local_flush_tlb_all();
 
                /*
                 * Restore per-cpu offset before any kernel
arch/arm64/mm/context.c +2 -2
index d70ff14dbdbd..48b53fb381af 100644
@@ -48,9 +48,9 @@ static void flush_context(void)
 {
        /* set the reserved TTBR0 before flushing the TLB */
        cpu_set_reserved_ttbr0();
-       flush_tlb_all();
+       local_flush_tlb_all();
        if (icache_is_aivivt())
-               __flush_icache_all();
+               __local_flush_icache_all();
 }
 
 static void set_mm_context(struct mm_struct *mm, unsigned int asid)
arch/arm64/mm/mmu.c +1 -2
index 9211b8527f25..71a310478c9e 100644
@@ -456,7 +456,7 @@ void __init paging_init(void)
         * point to zero page to avoid speculatively fetching new entries.
         */
        cpu_set_reserved_ttbr0();
-       flush_tlb_all();
+       local_flush_tlb_all();
        cpu_set_default_tcr_t0sz();
 }
 
--

2.1.4

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#7 lore raw

PATCH v2 4/10 arm64: mm: rewrite ASID allocator and MM context-switching code

In reply to Will Deacon

Our current switch_mm implementation suffers from a number of problems:

(1) The ASID allocator relies on IPIs to synchronise the CPUs on a rollover event

(2) Because of (1), we cannot allocate ASIDs with interrupts disabled and therefore make use of a TIF_SWITCH_MM flag to postpone the actual switch to finish_arch_post_lock_switch

(3) We run context switch with a reserved (invalid) TTBR0 value, even though the ASID and pgd are updated atomically

(4) We take a global spinlock (cpu_asid_lock) during context-switch

(5) We use h/w broadcast TLB operations when they are not required (e.g. in flush_context)

This patch addresses these problems by rewriting the ASID algorithm to match the bitmap-based arch/arm/ implementation more closely. This in turn allows us to remove much of the complications surrounding switch_mm, including the ugly thread flag.

---
 arch/arm64/include/asm/mmu.h         |  15 +--
 arch/arm64/include/asm/mmu_context.h |  76 ++---------
 arch/arm64/include/asm/thread_info.h |   1 -
 arch/arm64/kernel/asm-offsets.c      |   2 +-
 arch/arm64/kernel/efi.c              |   1 -
 arch/arm64/mm/context.c              | 238 +++++++++++++++++++++--------------
 arch/arm64/mm/proc.S                 |   2 +-
 7 files changed, 166 insertions(+), 169 deletions(-)
arch/arm64/include/asm/mmu.h +8 -7
index 030208767185..990124a67eeb 100644
@@ -17,15 +17,16 @@
 #define __ASM_MMU_H
 
 typedef struct {
-	unsigned int id;
-	raw_spinlock_t id_lock;
-	void *vdso;
+	atomic64_t	id;
+	void		*vdso;
 } mm_context_t;
 
-#define INIT_MM_CONTEXT(name) \
-	.context.id_lock = __RAW_SPIN_LOCK_UNLOCKED(name.context.id_lock),
-
-#define ASID(mm)	((mm)->context.id & 0xffff)
+/*
+ * This macro is only used by the TLBI code, which cannot race with an
+ * ASID change and therefore doesn't need to reload the counter using
+ * atomic64_read.
+ */
+#define ASID(mm)	((mm)->context.id.counter & 0xffff)
 
 extern void paging_init(void);
 extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
arch/arm64/include/asm/mmu_context.h +11 -65

90 lines not on this page · raw message

arch/arm64/include/asm/thread_info.h +0 -1
index dcd06d18a42a..555c6dec5ef2 100644
@@ -111,7 +111,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_RESTORE_SIGMASK	20
 #define TIF_SINGLESTEP		21
 #define TIF_32BIT		22	/* 32bit process */
-#define TIF_SWITCH_MM		23	/* deferred switch_mm */
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
arch/arm64/kernel/asm-offsets.c +1 -1
index 8d89cf8dae55..25de8b244961 100644
@@ -60,7 +60,7 @@ int main(void)
   DEFINE(S_SYSCALLNO,		offsetof(struct pt_regs, syscallno));
   DEFINE(S_FRAME_SIZE,		sizeof(struct pt_regs));
   BLANK();
-  DEFINE(MM_CONTEXT_ID,		offsetof(struct mm_struct, context.id));
+  DEFINE(MM_CONTEXT_ID,		offsetof(struct mm_struct, context.id.counter));
   BLANK();
   DEFINE(VMA_VM_MM,		offsetof(struct vm_area_struct, vm_mm));
   DEFINE(VMA_VM_FLAGS,		offsetof(struct vm_area_struct, vm_flags));
arch/arm64/kernel/efi.c +0 -1
index 4d12926ea40d..a48d1f477b2e 100644
@@ -48,7 +48,6 @@ static struct mm_struct efi_mm = {
 	.mmap_sem		= __RWSEM_INITIALIZER(efi_mm.mmap_sem),
 	.page_table_lock	= __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
 	.mmlist			= LIST_HEAD_INIT(efi_mm.mmlist),
-	INIT_MM_CONTEXT(efi_mm)
 };
 
 static int uefi_debug __initdata;
arch/arm64/mm/context.c +145 -93

280 lines not on this page · raw message

arch/arm64/mm/proc.S +1 -1
index bbde13d77da5..91cb2eaac256 100644
@@ -130,7 +130,7 @@ ENDPROC(cpu_do_resume)
  *	- pgd_phys - physical address of new TTB
  */
 ENTRY(cpu_do_switch_mm)
-	mmid	w1, x1				// get mm->context.id
+	mmid	x1, x1				// get mm->context.id
 	bfi	x0, x1, #48, #16		// set the ASID
 	msr	ttbr0_el1, x0			// set TTBR0
 	isb
Signature · 1 line
2.1.4
#8 lore raw

PATCH v2 5/10 arm64: tlbflush: remove redundant ASID casts to (unsigned long)

In reply to Will Deacon

The ASID macro returns a 64-bit (long long) value, so there is no need to cast to (unsigned long) before shifting prior to a TLBI operation.

---
 arch/arm64/include/asm/tlbflush.h | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)
arch/arm64/include/asm/tlbflush.h +4 -5
index 96f944e75dc4..93e9f964805c 100644
@@ -81,7 +81,7 @@ static inline void flush_tlb_all(void)
 
 static inline void flush_tlb_mm(struct mm_struct *mm)
 {
-	unsigned long asid = (unsigned long)ASID(mm) << 48;
+	unsigned long asid = ASID(mm) << 48;
 
 	dsb(ishst);
 	asm("tlbi	aside1is, %0" : : "r" (asid));
@@ -91,8 +91,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
 static inline void flush_tlb_page(struct vm_area_struct *vma,
 				  unsigned long uaddr)
 {
-	unsigned long addr = uaddr >> 12 |
-		((unsigned long)ASID(vma->vm_mm) << 48);
+	unsigned long addr = uaddr >> 12 | (ASID(vma->vm_mm) << 48);
 
 	dsb(ishst);
 	asm("tlbi	vale1is, %0" : : "r" (addr));
@@ -109,7 +108,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
 				     unsigned long start, unsigned long end,
 				     bool last_level)
 {
-	unsigned long asid = (unsigned long)ASID(vma->vm_mm) << 48;
+	unsigned long asid = ASID(vma->vm_mm) << 48;
 	unsigned long addr;
 
 	if ((end - start) > MAX_TLB_RANGE) {
@@ -162,7 +161,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
 static inline void __flush_tlb_pgtable(struct mm_struct *mm,
 				       unsigned long uaddr)
 {
-	unsigned long addr = uaddr >> 12 | ((unsigned long)ASID(mm) << 48);
+	unsigned long addr = uaddr >> 12 | (ASID(mm) << 48);
 
 	dsb(ishst);
 	asm("tlbi	vae1is, %0" : : "r" (addr));
Signature · 1 line
2.1.4
#9 lore raw

PATCH v2 6/10 arm64: tlbflush: avoid flushing when fullmm == 1

In reply to Will Deacon

The TLB gather code sets fullmm=1 when tearing down the entire address space for an mm_struct on exit or execve. Given that the ASID allocator will never re-allocate a dirty ASID, this flushing is not needed and can simply be avoided in the flushing code.

---
 arch/arm64/include/asm/tlb.h | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)
arch/arm64/include/asm/tlb.h +15 -11
index d6e6b6660380..ffdaea7954bb 100644
@@ -37,17 +37,21 @@ static inline void __tlb_remove_table(void *_table)
 
 static inline void tlb_flush(struct mmu_gather *tlb)
 {
-	if (tlb->fullmm) {
-		flush_tlb_mm(tlb->mm);
-	} else {
-		struct vm_area_struct vma = { .vm_mm = tlb->mm, };
-		/*
-		 * The intermediate page table levels are already handled by
-		 * the __(pte|pmd|pud)_free_tlb() functions, so last level
-		 * TLBI is sufficient here.
-		 */
-		__flush_tlb_range(&vma, tlb->start, tlb->end, true);
-	}
+	struct vm_area_struct vma = { .vm_mm = tlb->mm, };
+
+	/*
+	 * The ASID allocator will either invalidate the ASID or mark
+	 * it as used.
+	 */
+	if (tlb->fullmm)
+		return;
+
+	/*
+	 * The intermediate page table levels are already handled by
+	 * the __(pte|pmd|pud)_free_tlb() functions, so last level
+	 * TLBI is sufficient here.
+	 */
+	__flush_tlb_range(&vma, tlb->start, tlb->end, true);
 }
 
 static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
Signature · 1 line
2.1.4
#10 lore raw

PATCH v2 7/10 arm64: switch_mm: simplify mm and CPU checks

In reply to Will Deacon

switch_mm performs some checks to try and avoid entering the ASID allocator:

(1) If we're switching to the init_mm (no user mappings), then simply set a reserved TTBR0 value with no page table (the zero page)

(2) If prev == next *and* the mm_cpumask indicates that we've run on this CPU before, then we can skip the allocator.

However, there is plenty of redundancy here. With the new ASID allocator, if prev == next, then we know that our ASID is valid and do not need to worry about re-allocation. Consequently, we can drop the mm_cpumask check in (2) and move the prev == next check before the init_mm check, since if prev == next == init_mm then there's nothing to do.

---
 arch/arm64/include/asm/mmu_context.h | 6 ++++--
 arch/arm64/mm/context.c              | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)
arch/arm64/include/asm/mmu_context.h +4 -2
index f4c74a951b6c..c0e87898ba96 100644
@@ -129,6 +129,9 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
 {
 	unsigned int cpu = smp_processor_id();
 
+	if (prev == next)
+		return;
+
 	/*
 	 * init_mm.pgd does not contain any user mappings and it is always
 	 * active for kernel addresses in TTBR1. Just set the reserved TTBR0.
@@ -138,8 +141,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
 		return;
 	}
 
-	if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
-		check_and_switch_context(next, tsk);
+	check_and_switch_context(next, cpu);
 }
 
 #define deactivate_mm(tsk,mm)	do { } while (0)
arch/arm64/mm/context.c +1 -1
index e902229b1a3d..4b9ec4484e3f 100644
@@ -166,10 +166,10 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
 		local_flush_tlb_all();
 
 	atomic64_set(&per_cpu(active_asids, cpu), asid);
-	cpumask_set_cpu(cpu, mm_cpumask(mm));
 	raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
 
 switch_mm_fastpath:
+	cpumask_set_cpu(cpu, mm_cpumask(mm));
 	cpu_switch_mm(mm->pgd, mm);
 }
 
Signature · 1 line
2.1.4
#11 lore raw

PATCH v2 8/10 arm64: mm: kill mm_cpumask usage

In reply to Will Deacon

mm_cpumask isn't actually used for anything on arm64, so remove all the code trying to keep it up-to-date.

---
 arch/arm64/kernel/smp.c | 7 -------
 arch/arm64/mm/context.c | 2 --
 2 files changed, 9 deletions(-)
arch/arm64/kernel/smp.c +0 -7
index fdd4d4dbd64f..03b0aa28ea61 100644
@@ -142,7 +142,6 @@ asmlinkage void secondary_start_kernel(void)
 	 */
 	atomic_inc(&mm->mm_count);
 	current->active_mm = mm;
-	cpumask_set_cpu(cpu, mm_cpumask(mm));
 
 	set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
 	printk("CPU%u: Booted secondary processor\n", cpu);
@@ -233,12 +232,6 @@ int __cpu_disable(void)
 	 * OK - migrate IRQs away from this CPU
 	 */
 	migrate_irqs();
-
-	/*
-	 * Remove this CPU from the vm mask set of all processes.
-	 */
-	clear_tasks_mm_cpumask(cpu);
-
 	return 0;
 }
 
arch/arm64/mm/context.c +0 -2
index 4b9ec4484e3f..f636a2639f03 100644
@@ -132,7 +132,6 @@ set_asid:
 
 bump_gen:
 	asid |= generation;
-	cpumask_clear(mm_cpumask(mm));
 	return asid;
 }
 
@@ -169,7 +168,6 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
 	raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
 
 switch_mm_fastpath:
-	cpumask_set_cpu(cpu, mm_cpumask(mm));
 	cpu_switch_mm(mm->pgd, mm);
 }
 
Signature · 1 line
2.1.4
#12 lore raw

PATCH v2 9/10 arm64: tlb: remove redundant barrier from __flush_tlb_pgtable

In reply to Will Deacon

__flush_tlb_pgtable is used to invalidate intermediate page table entries after they have been cleared and are about to be freed. Since pXd_clear imply memory barriers, we don't need the extra one here.

---
 arch/arm64/include/asm/tlbflush.h | 1 -
 1 file changed, 1 deletion(-)
arch/arm64/include/asm/tlbflush.h +0 -1
index 93e9f964805c..b460ae28e346 100644
@@ -163,7 +163,6 @@ static inline void __flush_tlb_pgtable(struct mm_struct *mm,
 {
 	unsigned long addr = uaddr >> 12 | (ASID(mm) << 48);
 
-	dsb(ishst);
 	asm("tlbi	vae1is, %0" : : "r" (addr));
 	dsb(ish);
 }
Signature · 1 line
2.1.4
#13 lore raw

PATCH v2 10/10 arm64: mm: remove dsb from update_mmu_cache

In reply to Will Deacon

update_mmu_cache() consists of a dsb(ishst) instruction so that new user mappings are guaranteed to be visible to the page table walker on exception return.

In reality this can be a very expensive operation which is rarely needed. Removing this barrier shows a modest improvement in hackbench scores and , in the worst case, we re-take the user fault and establish that there was nothing to do.

---
 arch/arm64/include/asm/pgtable.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/include/asm/pgtable.h +3 -3
index 26b066690593..0d18e88e1cfa 100644
@@ -646,10 +646,10 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
 				    unsigned long addr, pte_t *ptep)
 {
 	/*
-	 * set_pte() does not have a DSB for user mappings, so make sure that
-	 * the page table write is visible.
+	 * We don't do anything here, so there's a very small chance of
+	 * us retaking a user fault which we just fixed up. The alternative
+	 * is doing a dsb(ishst), but that penalises the fastpath.
 	 */
-	dsb(ishst);
 }
 
 #define update_mmu_cache_pmd(vma, address, pmd) do { } while (0)
Signature · 1 line
2.1.4
maintainer
#14 lore raw

v2 cover arm64 switch_mm improvements

In reply to Will Deacon
Quoted 11 lines · Will Deacon

Will Deacon (10):
arm64: mm: remove unused cpu_set_idmap_tcr_t0sz function
arm64: proc: de-scope TLBI operation during cold boot
arm64: flush: use local TLB and I-cache invalidation
arm64: mm: rewrite ASID allocator and MM context-switching code
arm64: tlbflush: remove redundant ASID casts to (unsigned long)
arm64: tlbflush: avoid flushing when fullmm == 1
arm64: switch_mm: simplify mm and CPU checks
arm64: mm: kill mm_cpumask usage
arm64: tlb: remove redundant barrier from __flush_tlb_pgtable
arm64: mm: remove dsb from update_mmu_cache

Series queued for 4.4. Thanks.

Signature · 1 line
Catalin