Skip to contentSkip to conversation

Fixes for SLB to C series

PATCH New Nicholas Piggin 1 participant 10 messages Oct 2, 2018

Subsystems (2)
lore
Share & tools
lore

Nicholas Piggin

author reviewer
#0 lore raw

v2 cover Fixes for SLB to C series

This is another spin of the fixes. Also painfully re-discovered that we need https://patchwork.ozlabs.org/patch/962327/, as it prevents POWER8 NUMA from booting (initial stack SLB for the !0 node CPUs gets cleared by the initial TLB flush without it, and the SLB handler now uses the stack...)

Question is whether to revert the series and try again next time. I'm inclined to maybe say revert because it's been holdig up the tree a bit and also some of these patches like the PPR fixes are pretty complicated and should really be done as pre-requisites rather than fixes.

Thanks,
Nick

Nicholas Piggin (9):
Revert "powerpc/64s/hash: remove user SLB data from the paca" powerpc/64: add struct int_regs to save additional registers on stack powerpc/64: interrupts save PPR on stack rather than thread_struct powerpc/64s/hash: Fix preloading of SLB entries powerpc/64s/hash: add more barriers for slb preloading powerpc/64s/hash: SLB fix preemption issues powerpc/64s/hash: Fix false positive preempt warnings powerpc/64s/hash: slb_flush_and_rebolt simplify powerpc/64s/hash: add some slb debugging tests

 arch/powerpc/include/asm/book3s/64/mmu-hash.h |   3 +-
 arch/powerpc/include/asm/exception-64s.h      |   9 +-
 arch/powerpc/include/asm/paca.h               |  16 ++
 arch/powerpc/include/asm/processor.h          |  12 +-
 arch/powerpc/include/asm/ptrace.h             |  18 +-
 arch/powerpc/kernel/asm-offsets.c             |  32 ++-
 arch/powerpc/kernel/entry_64.S                |  15 +-
 arch/powerpc/kernel/paca.c                    |  22 ++
 arch/powerpc/kernel/process.c                 |  54 ++---
 arch/powerpc/kernel/ptrace.c                  |   4 +-
 arch/powerpc/kernel/stacktrace.c              |   2 +-
 arch/powerpc/kernel/swsusp_asm64.S            |   2 +-
 arch/powerpc/mm/hash_utils_64.c               |  46 +++-
 arch/powerpc/mm/mmu_context.c                 |   3 +-
 arch/powerpc/mm/slb.c                         | 203 ++++++++++++------
 arch/powerpc/mm/slice.c                       |  29 ++-
 16 files changed, 314 insertions(+), 156 deletions(-)
Signature · 1 line
2.18.0
author reviewer
#1 lore raw

PATCH v2 1/9 Revert "powerpc/64s/hash: remove user SLB data from the paca"

This reverts commit 8fed04d0f6aedf99b3d811ba58d38bb7f938a47a.

There are a number of problems with this patch, there are minor bugs like vmalloc_sllp update no longer being picked up into pacas, but more fundamentally the SLB flush can not be broadcast out to other CPUs because it must be done in places where interrupts can be disabled, which would deadlock.

Rather than try put some bandaids on this, the best fix is to revert it. I think it's a worthwhile approach, but it should be solved by demotig things eagerly at setup or map time, rather than in low level hash faults. Most cases are rarely used by software or apply to very old hardware.

---
 arch/powerpc/include/asm/book3s/64/mmu-hash.h |  1 -
 arch/powerpc/include/asm/paca.h               | 13 ++++++
 arch/powerpc/kernel/asm-offsets.c             |  9 ++++
 arch/powerpc/kernel/paca.c                    | 22 +++++++++
 arch/powerpc/mm/hash_utils_64.c               | 46 ++++++++++++++-----
 arch/powerpc/mm/mmu_context.c                 |  3 +-
 arch/powerpc/mm/slb.c                         | 20 +-------
 arch/powerpc/mm/slice.c                       | 29 ++++++++----
 8 files changed, 103 insertions(+), 40 deletions(-)
arch/powerpc/include/asm/book3s/64/mmu-hash.h +0 -1
index bbeaf6adf93c..14e552ea5e52 100644
@@ -503,7 +503,6 @@ struct slb_entry {
 };
 
 extern void slb_initialize(void);
-extern void core_flush_all_slbs(struct mm_struct *mm);
 extern void slb_flush_and_rebolt(void);
 void slb_flush_all_realmode(void);
 void __slb_restore_bolted_realmode(void);
arch/powerpc/include/asm/paca.h +13 -0
index 6d6b3706232c..4838149ee07b 100644
@@ -146,6 +146,18 @@ struct paca_struct {
 	struct tlb_core_data tcd;
 #endif /* CONFIG_PPC_BOOK3E */
 
+#ifdef CONFIG_PPC_BOOK3S
+	mm_context_id_t mm_ctx_id;
+#ifdef CONFIG_PPC_MM_SLICES
+	unsigned char mm_ctx_low_slices_psize[BITS_PER_LONG / BITS_PER_BYTE];
+	unsigned char mm_ctx_high_slices_psize[SLICE_ARRAY_SIZE];
+	unsigned long mm_ctx_slb_addr_limit;
+#else
+	u16 mm_ctx_user_psize;
+	u16 mm_ctx_sllp;
+#endif
+#endif
+
 	/*
 	 * then miscellaneous read-write fields
 	 */
@@ -248,6 +260,7 @@ struct paca_struct {
 #endif /* CONFIG_PPC_BOOK3S_64 */
 } ____cacheline_aligned;
 
+extern void copy_mm_to_paca(struct mm_struct *mm);
 extern struct paca_struct **paca_ptrs;
 extern void initialise_paca(struct paca_struct *new_paca, int cpu);
 extern void setup_paca(struct paca_struct *new_paca);
arch/powerpc/kernel/asm-offsets.c +9 -0
index ba9d0fc98730..27c78b6c9909 100644
@@ -180,6 +180,15 @@ int main(void)
 	OFFSET(PACAIRQSOFTMASK, paca_struct, irq_soft_mask);
 	OFFSET(PACAIRQHAPPENED, paca_struct, irq_happened);
 	OFFSET(PACA_FTRACE_ENABLED, paca_struct, ftrace_enabled);
+#ifdef CONFIG_PPC_BOOK3S
+	OFFSET(PACACONTEXTID, paca_struct, mm_ctx_id);
+#ifdef CONFIG_PPC_MM_SLICES
+	OFFSET(PACALOWSLICESPSIZE, paca_struct, mm_ctx_low_slices_psize);
+	OFFSET(PACAHIGHSLICEPSIZE, paca_struct, mm_ctx_high_slices_psize);
+	OFFSET(PACA_SLB_ADDR_LIMIT, paca_struct, mm_ctx_slb_addr_limit);
+	DEFINE(MMUPSIZEDEFSIZE, sizeof(struct mmu_psize_def));
+#endif /* CONFIG_PPC_MM_SLICES */
+#endif
 
 #ifdef CONFIG_PPC_BOOK3E
 	OFFSET(PACAPGD, paca_struct, pgd);
arch/powerpc/kernel/paca.c +22 -0
index 0cf84e30d1cd..0ee3e6d50f28 100644
@@ -258,3 +258,25 @@ void __init free_unused_pacas(void)
 	printk(KERN_DEBUG "Allocated %u bytes for %u pacas\n",
 			paca_ptrs_size + paca_struct_size, nr_cpu_ids);
 }
+
+void copy_mm_to_paca(struct mm_struct *mm)
+{
+#ifdef CONFIG_PPC_BOOK3S
+	mm_context_t *context = &mm->context;
+
+	get_paca()->mm_ctx_id = context->id;
+#ifdef CONFIG_PPC_MM_SLICES
+	VM_BUG_ON(!mm->context.slb_addr_limit);
+	get_paca()->mm_ctx_slb_addr_limit = mm->context.slb_addr_limit;
+	memcpy(&get_paca()->mm_ctx_low_slices_psize,
+	       &context->low_slices_psize, sizeof(context->low_slices_psize));
+	memcpy(&get_paca()->mm_ctx_high_slices_psize,
+	       &context->high_slices_psize, TASK_SLICE_ARRAY_SZ(mm));
+#else /* CONFIG_PPC_MM_SLICES */
+	get_paca()->mm_ctx_user_psize = context->user_psize;
+	get_paca()->mm_ctx_sllp = context->sllp;
+#endif
+#else /* !CONFIG_PPC_BOOK3S */
+	return;
+#endif
+}
arch/powerpc/mm/hash_utils_64.c +35 -11

111 lines not on this page · raw message

arch/powerpc/mm/mmu_context.c +2 -1
index 28ae2835db3d..f84e14f23e50 100644
@@ -54,7 +54,8 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
 		 * MMU context id, which is then moved to SPRN_PID.
 		 *
 		 * For the hash MMU it is either the first load from slb_cache
-		 * in switch_slb(), and/or load of MMU context id.
+		 * in switch_slb(), and/or the store of paca->mm_ctx_id in
+		 * copy_mm_to_paca().
 		 *
 		 * On the other side, the barrier is in mm/tlb-radix.c for
 		 * radix which orders earlier stores to clear the PTEs vs
arch/powerpc/mm/slb.c +2 -18
index b438220c4336..74c3b6f8e9b7 100644
@@ -430,6 +430,8 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 	}
 	get_paca()->slb_used_bitmap = get_paca()->slb_kern_bitmap;
 
+	copy_mm_to_paca(mm);
+
 	/*
 	 * We gradually age out SLBs after a number of context switches to
 	 * reduce reload overhead of unused entries (like we do with FP/VEC
@@ -460,24 +462,6 @@ void slb_set_size(u16 size)
 	mmu_slb_size = size;
 }
 
-static void cpu_flush_slb(void *parm)
-{
-	struct mm_struct *mm = parm;
-	unsigned long flags;
-
-	if (mm != current->active_mm)
-		return;
-
-	local_irq_save(flags);
-	slb_flush_and_rebolt();
-	local_irq_restore(flags);
-}
-
-void core_flush_all_slbs(struct mm_struct *mm)
-{
-	on_each_cpu(cpu_flush_slb, mm, 1);
-}
-
 void slb_initialize(void)
 {
 	unsigned long linear_llp, vmalloc_llp, io_llp;
arch/powerpc/mm/slice.c +20 -9
index fc5b3a1ec666..546dd07c8083 100644
@@ -207,6 +207,23 @@ static bool slice_check_range_fits(struct mm_struct *mm,
 	return true;
 }
 
+static void slice_flush_segments(void *parm)
+{
+#ifdef CONFIG_PPC64
+	struct mm_struct *mm = parm;
+	unsigned long flags;
+
+	if (mm != current->active_mm)
+		return;
+
+	copy_mm_to_paca(current->active_mm);
+
+	local_irq_save(flags);
+	slb_flush_and_rebolt();
+	local_irq_restore(flags);
+#endif
+}
+
 static void slice_convert(struct mm_struct *mm,
 				const struct slice_mask *mask, int psize)
 {
@@ -272,9 +289,6 @@ static void slice_convert(struct mm_struct *mm,
 	spin_unlock_irqrestore(&slice_convert_lock, flags);
 
 	copro_flush_all_slbs(mm);
-#ifdef CONFIG_PPC64
-	core_flush_all_slbs(mm);
-#endif
 }
 
 /*
@@ -488,9 +502,8 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
 		 * be already initialised beyond the old address limit.
 		 */
 		mm->context.slb_addr_limit = high_limit;
-#ifdef CONFIG_PPC64
-		core_flush_all_slbs(mm);
-#endif
+
+		on_each_cpu(slice_flush_segments, mm, 1);
 	}
 
 	/* Sanity checks */
@@ -652,10 +665,8 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
 		(SLICE_NUM_HIGH &&
 		 !bitmap_empty(potential_mask.high_slices, SLICE_NUM_HIGH))) {
 		slice_convert(mm, &potential_mask, psize);
-#ifdef CONFIG_PPC64
 		if (psize > MMU_PAGE_BASE)
-			core_flush_all_slbs(mm);
-#endif
+			on_each_cpu(slice_flush_segments, mm, 1);
 	}
 	return newaddr;
 
Signature · 1 line
2.18.0
author reviewer
#2 lore raw

PATCH v2 2/9 powerpc/64: add struct int_regs to save additional registers on stack

In reply to Nicholas Piggin

struct pt_regs is part of the user ABI and also the fundametal structure for saving registers at interrupt.

The generic kernel code makes it difficult to completely decouple these, but it's easy enough to add additional space required to save more registers. Create a struct int_stack with struct pt_regs at offset 0.

This is required for a following fix to save the PPR SPR on stack.

---
 arch/powerpc/include/asm/ptrace.h | 17 +++++++---
 arch/powerpc/kernel/asm-offsets.c | 21 ++++++++-----
 arch/powerpc/kernel/process.c     | 52 ++++++++++++++++---------------
 arch/powerpc/kernel/stacktrace.c  |  2 +-
 4 files changed, 53 insertions(+), 39 deletions(-)
arch/powerpc/include/asm/ptrace.h +12 -5
index 447cbd1bee99..1a98cd8c49f6 100644
@@ -26,7 +26,6 @@
 #include <uapi/asm/ptrace.h>
 #include <asm/asm-const.h>
 
-
 #ifdef __powerpc64__
 
 /*
@@ -44,7 +43,7 @@
 #define STACK_FRAME_OVERHEAD	112	/* size of minimum stack frame */
 #define STACK_FRAME_LR_SAVE	2	/* Location of LR in stack frame */
 #define STACK_FRAME_REGS_MARKER	ASM_CONST(0x7265677368657265)
-#define STACK_INT_FRAME_SIZE	(sizeof(struct pt_regs) + \
+#define STACK_INT_FRAME_SIZE	(sizeof(struct int_regs) + \
 				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
 #define STACK_FRAME_MARKER	12
 
@@ -76,6 +75,11 @@
 
 #ifndef __ASSEMBLY__
 
+struct int_regs {
+	/* pt_regs must be offset 0 so r1 + STACK_FRAME_OVERHEAD points to it */
+	struct pt_regs pt_regs;
+};
+
 #define GET_IP(regs)		((regs)->nip)
 #define GET_USP(regs)		((regs)->gpr[1])
 #define GET_FP(regs)		(0)
@@ -119,8 +123,11 @@ extern int ptrace_get_reg(struct task_struct *task, int regno,
 extern int ptrace_put_reg(struct task_struct *task, int regno,
 			  unsigned long data);
 
-#define current_pt_regs() \
-	((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
+#define current_int_regs() \
+ ((struct int_regs *)((unsigned long)current_thread_info() + THREAD_SIZE) - 1)
+
+#define current_pt_regs() (&current_int_regs()->pt_regs)
+
 /*
  * We use the least-significant bit of the trap field to indicate
  * whether we have saved the full set of registers, or only a
@@ -137,7 +144,7 @@ extern int ptrace_put_reg(struct task_struct *task, int regno,
 #define TRAP(regs)		((regs)->trap & ~0xF)
 #ifdef __powerpc64__
 #define NV_REG_POISON		0xdeadbeefdeadbeefUL
-#define CHECK_FULL_REGS(regs)	BUG_ON(regs->trap & 1)
+#define CHECK_FULL_REGS(regs)	BUG_ON((regs)->trap & 1)
 #else
 #define NV_REG_POISON		0xdeadbeef
 #define CHECK_FULL_REGS(regs)						      \
arch/powerpc/kernel/asm-offsets.c +13 -8
index 27c78b6c9909..509a113f97dc 100644
@@ -72,8 +72,13 @@
 #include <asm/fixmap.h>
 #endif
 
-#define STACK_PT_REGS_OFFSET(sym, val)	\
-	DEFINE(sym, STACK_FRAME_OVERHEAD + offsetof(struct pt_regs, val))
+#define STACK_INT_REGS_OFFSET(sym, val)				\
+	DEFINE(sym, STACK_FRAME_OVERHEAD + offsetof(struct int_regs, val))
+
+#define STACK_PT_REGS_OFFSET(sym, val)				\
+	DEFINE(sym, STACK_FRAME_OVERHEAD +			\
+			offsetof(struct int_regs, pt_regs) +	\
+			offsetof(struct pt_regs, val))
 
 int main(void)
 {
@@ -150,7 +155,7 @@ int main(void)
 	OFFSET(THREAD_CKFPSTATE, thread_struct, ckfp_state.fpr);
 	/* Local pt_regs on stack for Transactional Memory funcs. */
 	DEFINE(TM_FRAME_SIZE, STACK_FRAME_OVERHEAD +
-	       sizeof(struct pt_regs) + 16);
+	       sizeof(struct int_regs) + 16);
 #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
 
 	OFFSET(TI_FLAGS, thread_info, flags);
@@ -273,11 +278,11 @@ int main(void)
 
 	/* Interrupt register frame */
 	DEFINE(INT_FRAME_SIZE, STACK_INT_FRAME_SIZE);
-	DEFINE(SWITCH_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs));
+	DEFINE(SWITCH_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct int_regs));
 #ifdef CONFIG_PPC64
 	/* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */
-	DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16);
-	DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16);
+	DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct int_regs) + 16);
+	DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct int_regs) + 16);
 #endif /* CONFIG_PPC64 */
 	STACK_PT_REGS_OFFSET(GPR0, gpr[0]);
 	STACK_PT_REGS_OFFSET(GPR1, gpr[1]);
@@ -324,8 +329,8 @@ int main(void)
 	STACK_PT_REGS_OFFSET(SOFTE, softe);
 
 	/* These _only_ to be used with {PROM,RTAS}_FRAME_SIZE!!! */
-	DEFINE(_SRR0, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs));
-	DEFINE(_SRR1, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs)+8);
+	DEFINE(_SRR0, STACK_FRAME_OVERHEAD+sizeof(struct int_regs));
+	DEFINE(_SRR1, STACK_FRAME_OVERHEAD+sizeof(struct int_regs)+8);
 #endif /* CONFIG_PPC64 */
 
 #if defined(CONFIG_PPC32)
arch/powerpc/kernel/process.c +27 -25

112 lines not on this page · raw message

arch/powerpc/kernel/stacktrace.c +1 -1
index e2c50b55138f..463dedc4d664 100644
@@ -120,7 +120,7 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
 		 * an unreliable stack trace until it's been
 		 * _switch()'ed to for the first time.
 		 */
-		stack_end -= STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
+		stack_end -= STACK_FRAME_OVERHEAD + sizeof(struct int_regs);
 	} else {
 		/*
 		 * idle tasks have a custom stack layout,
Signature · 1 line
2.18.0
author reviewer
#3 lore raw

PATCH v2 3/9 powerpc/64: interrupts save PPR on stack rather than thread_struct

In reply to Nicholas Piggin

PPR is the odd register out when it comes to interrupt handling, it is saved in current->thread.ppr while all others are saved on the stack.

The difficulty with this is that accessing thread.ppr can cause a SLB fault, but the SLB fault handler implementation in C change had assumed the normal exception entry handlers would not cause an SLB fault.

Fix this by allocating room in the interrupt stack to save PPR.

Fixes 5e46e29e6a97 ("powerpc/64s/hash: convert SLB miss handlers to C")
---
 arch/powerpc/include/asm/exception-64s.h |  9 ++++-----
 arch/powerpc/include/asm/processor.h     | 12 +++++++-----
 arch/powerpc/include/asm/ptrace.h        |  1 +
 arch/powerpc/kernel/asm-offsets.c        |  2 +-
 arch/powerpc/kernel/entry_64.S           | 15 +++++----------
 arch/powerpc/kernel/process.c            |  2 +-
 arch/powerpc/kernel/ptrace.c             |  4 ++--
 7 files changed, 21 insertions(+), 24 deletions(-)
arch/powerpc/include/asm/exception-64s.h +4 -5
index 47578b79f0fb..3b4767ed3ec5 100644
@@ -228,11 +228,10 @@
  * PPR save/restore macros used in exceptions_64s.S  
  * Used for P7 or later processors
  */
-#define SAVE_PPR(area, ra, rb)						\
+#define SAVE_PPR(area, ra)						\
 BEGIN_FTR_SECTION_NESTED(940)						\
-	ld	ra,PACACURRENT(r13);					\
-	ld	rb,area+EX_PPR(r13);	/* Read PPR from paca */	\
-	std	rb,TASKTHREADPPR(ra);					\
+	ld	ra,area+EX_PPR(r13);	/* Read PPR from paca */	\
+	std	ra,_PPR(r1);						\
 END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,940)
 
 #define RESTORE_PPR_PACA(area, ra)					\
@@ -500,7 +499,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
 3:	EXCEPTION_PROLOG_COMMON_1();					   \
 	beq	4f;			/* if from kernel mode		*/ \
 	ACCOUNT_CPU_USER_ENTRY(r13, r9, r10);				   \
-	SAVE_PPR(area, r9, r10);					   \
+	SAVE_PPR(area, r9);						   \
 4:	EXCEPTION_PROLOG_COMMON_2(area)					   \
 	EXCEPTION_PROLOG_COMMON_3(n)					   \
 	ACCOUNT_STOLEN_TIME
arch/powerpc/include/asm/processor.h +7 -5
index 350c584ca179..07251598056c 100644
@@ -32,9 +32,9 @@
 /* Default SMT priority is set to 3. Use 11- 13bits to save priority. */
 #define PPR_PRIORITY 3
 #ifdef __ASSEMBLY__
-#define INIT_PPR (PPR_PRIORITY << 50)
+#define DEFAULT_PPR (PPR_PRIORITY << 50)
 #else
-#define INIT_PPR ((u64)PPR_PRIORITY << 50)
+#define DEFAULT_PPR ((u64)PPR_PRIORITY << 50)
 #endif /* __ASSEMBLY__ */
 #endif /* CONFIG_PPC64 */
 
@@ -247,7 +247,11 @@ struct thread_struct {
 #ifdef CONFIG_PPC64
 	unsigned long	ksp_vsid;
 #endif
-	struct pt_regs	*regs;		/* Pointer to saved register state */
+	union {
+		struct int_regs	*iregs;	/* Pointer to saved register state */
+		struct pt_regs	*regs;	/* Pointer to saved register state */
+	};
+
 	mm_segment_t	addr_limit;	/* for get_fs() validation */
 #ifdef CONFIG_BOOKE
 	/* BookE base exception scratch space; align on cacheline */
@@ -342,7 +346,6 @@ struct thread_struct {
 	 * onwards.
 	 */
 	int		dscr_inherit;
-	unsigned long	ppr;	/* used to save/restore SMT priority */
 	unsigned long	tidr;
 #endif
 #ifdef CONFIG_PPC_BOOK3S_64
@@ -390,7 +393,6 @@ struct thread_struct {
 	.regs = (struct pt_regs *)INIT_SP - 1, /* XXX bogus, I think */ \
 	.addr_limit = KERNEL_DS, \
 	.fpexc_mode = 0, \
-	.ppr = INIT_PPR, \
 	.fscr = FSCR_TAR | FSCR_EBB \
 }
 #endif
arch/powerpc/include/asm/ptrace.h +1 -0
index 1a98cd8c49f6..9a5a1cc85bd0 100644
@@ -78,6 +78,7 @@
 struct int_regs {
 	/* pt_regs must be offset 0 so r1 + STACK_FRAME_OVERHEAD points to it */
 	struct pt_regs pt_regs;
+	unsigned long ppr;
 };
 
 #define GET_IP(regs)		((regs)->nip)
arch/powerpc/kernel/asm-offsets.c +1 -1
index 509a113f97dc..56241174ea94 100644
@@ -88,7 +88,6 @@ int main(void)
 #ifdef CONFIG_PPC64
 	DEFINE(SIGSEGV, SIGSEGV);
 	DEFINE(NMI_MASK, NMI_MASK);
-	OFFSET(TASKTHREADPPR, task_struct, thread.ppr);
 #else
 	OFFSET(THREAD_INFO, task_struct, stack);
 	DEFINE(THREAD_INFO_GAP, _ALIGN_UP(sizeof(struct thread_info), 16));
@@ -283,6 +282,7 @@ int main(void)
 	/* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */
 	DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct int_regs) + 16);
 	DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct int_regs) + 16);
+	STACK_INT_REGS_OFFSET(_PPR, ppr);
 #endif /* CONFIG_PPC64 */
 	STACK_PT_REGS_OFFSET(GPR0, gpr[0]);
 	STACK_PT_REGS_OFFSET(GPR1, gpr[1]);
arch/powerpc/kernel/entry_64.S +5 -10
index 77a888bfcb53..ce448f753d2e 100644
@@ -386,10 +386,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 
 4:	/* Anything else left to do? */
 BEGIN_FTR_SECTION
-	lis	r3,INIT_PPR@highest	/* Set thread.ppr = 3 */
-	ld	r10,PACACURRENT(r13)
+	lis	r3,DEFAULT_PPR@highest	/* Set default PPR */
 	sldi	r3,r3,32	/* bits 11-13 are used for ppr */
-	std	r3,TASKTHREADPPR(r10)
+	std	r3,_PPR(r1)
 END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 
 	andi.	r0,r9,(_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP)
@@ -938,12 +937,6 @@ fast_exception_return:
 	andi.	r0,r3,MSR_RI
 	beq-	.Lunrecov_restore
 
-	/* Load PPR from thread struct before we clear MSR:RI */
-BEGIN_FTR_SECTION
-	ld	r2,PACACURRENT(r13)
-	ld	r2,TASKTHREADPPR(r2)
-END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
-
 	/*
 	 * Clear RI before restoring r13.  If we are returning to
 	 * userspace and we take an exception after restoring r13,
@@ -964,7 +957,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 	andi.	r0,r3,MSR_PR
 	beq	1f
 BEGIN_FTR_SECTION
-	mtspr	SPRN_PPR,r2	/* Restore PPR */
+	/* Restore PPR */
+	ld	r2,_PPR(r1)
+	mtspr	SPRN_PPR,r2
 END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
 	ACCOUNT_CPU_USER_EXIT(r13, r2, r4)
 	REST_GPR(13, r1)
arch/powerpc/kernel/process.c +1 -1
index 532e9a83e526..18a14fb8f759 100644
@@ -1711,7 +1711,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
 		p->thread.dscr = mfspr(SPRN_DSCR);
 	}
 	if (cpu_has_feature(CPU_FTR_HAS_PPR))
-		p->thread.ppr = INIT_PPR;
+		childregs->ppr = DEFAULT_PPR;
 
 	p->thread.tidr = 0;
 #endif
arch/powerpc/kernel/ptrace.c +2 -2
index 9667666eb18e..57c04176f686 100644
@@ -1609,7 +1609,7 @@ static int ppr_get(struct task_struct *target,
 		      void *kbuf, void __user *ubuf)
 {
 	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
-				   &target->thread.ppr, 0, sizeof(u64));
+				   &target->thread.iregs->ppr, 0, sizeof(u64));
 }
 
 static int ppr_set(struct task_struct *target,
@@ -1618,7 +1618,7 @@ static int ppr_set(struct task_struct *target,
 		      const void *kbuf, const void __user *ubuf)
 {
 	return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
-				  &target->thread.ppr, 0, sizeof(u64));
+				  &target->thread.iregs->ppr, 0, sizeof(u64));
 }
 
 static int dscr_get(struct task_struct *target,
Signature · 1 line
2.18.0
author reviewer
#4 lore raw

PATCH v2 4/9 powerpc/64s/hash: Fix preloading of SLB entries

In reply to Nicholas Piggin

slb_setup_new_exec and preload_new_slb_context assumed if an address missed the preload cache, then it would not be in the SLB and could be added. This is wrong if the preload cache has started to overflow. This can cause SLB multi-hits on user addresses.

That assumption came from an earlier version of the patch which cleared the preload cache when copying the task, but even that was technically wrong because some user accesses occur before these preloads, and the preloads themselves could overflow the cache depending on the size.

Fixes 89ca4e126a3f ("powerpc/64s/hash: Add a SLB preload cache")
---
 arch/powerpc/mm/slb.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
arch/powerpc/mm/slb.c +11 -0
index 74c3b6f8e9b7..a5bd3c02d432 100644
@@ -311,6 +311,13 @@ void slb_setup_new_exec(void)
 	struct mm_struct *mm = current->mm;
 	unsigned long exec = 0x10000000;
 
+	/*
+	 * preload cache can only be used to determine whether a SLB
+	 * entry exists if it does not start to overflow.
+	 */
+	if (ti->slb_preload_nr + 2 > SLB_PRELOAD_NR)
+		return;
+
 	/*
 	 * We have no good place to clear the slb preload cache on exec,
 	 * flush_thread is about the earliest arch hook but that happens
@@ -345,6 +352,10 @@ void preload_new_slb_context(unsigned long start, unsigned long sp)
 	struct mm_struct *mm = current->mm;
 	unsigned long heap = mm->start_brk;
 
+	/* see above */
+	if (ti->slb_preload_nr + 3 > SLB_PRELOAD_NR)
+		return;
+
 	/* Userspace entry address. */
 	if (!is_kernel_addr(start)) {
 		if (preload_add(ti, start))
Signature · 1 line
2.18.0
author reviewer
#5 lore raw

PATCH v2 5/9 powerpc/64s/hash: add more barriers for slb preloading

In reply to Nicholas Piggin

In several places, more care has to be taken to prevent compiler or CPU re-ordering of memory accesses into critical sections that must not take SLB faults. Barriers are explained in the comments.

Fixes 5e46e29e6a97 ("powerpc/64s/hash: convert SLB miss handlers to C")
Fixes 89ca4e126a3f ("powerpc/64s/hash: Add a SLB preload cache")
---
 arch/powerpc/mm/slb.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)
arch/powerpc/mm/slb.c +31 -6
index a5bd3c02d432..8c38659f1b6b 100644
@@ -344,6 +344,9 @@ void slb_setup_new_exec(void)
 		if (preload_add(ti, mm->mmap_base))
 			slb_allocate_user(mm, mm->mmap_base);
 	}
+
+	/* see switch_slb */
+	asm volatile("isync" : : : "memory");
 }
 
 void preload_new_slb_context(unsigned long start, unsigned long sp)
@@ -373,6 +376,9 @@ void preload_new_slb_context(unsigned long start, unsigned long sp)
 		if (preload_add(ti, heap))
 			slb_allocate_user(mm, heap);
 	}
+
+	/* see switch_slb */
+	asm volatile("isync" : : : "memory");
 }
 
 
@@ -389,6 +395,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 	 * which would update the slb_cache/slb_cache_ptr fields in the PACA.
 	 */
 	hard_irq_disable();
+	asm volatile("isync" : : : "memory");
 	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
 		/*
 		 * SLBIA IH=3 invalidates all Class=1 SLBEs and their
@@ -396,7 +403,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 		 * switch_slb wants. So ARCH_300 does not use the slb
 		 * cache.
 		 */
-		asm volatile("isync ; " PPC_SLBIA(3)" ; isync");
+		asm volatile(PPC_SLBIA(3));
 	} else {
 		unsigned long offset = get_paca()->slb_cache_ptr;
 
@@ -404,7 +411,6 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 		    offset <= SLB_CACHE_ENTRIES) {
 			unsigned long slbie_data = 0;
 
-			asm volatile("isync" : : : "memory");
 			for (i = 0; i < offset; i++) {
 				/* EA */
 				slbie_data = (unsigned long)
@@ -419,7 +425,6 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 			if (!cpu_has_feature(CPU_FTR_ARCH_207S) && offset == 1)
 				asm volatile("slbie %0" : : "r" (slbie_data));
 
-			asm volatile("isync" : : : "memory");
 		} else {
 			struct slb_shadow *p = get_slb_shadow();
 			unsigned long ksp_esid_data =
@@ -427,8 +432,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 			unsigned long ksp_vsid_data =
 				be64_to_cpu(p->save_area[KSTACK_INDEX].vsid);
 
-			asm volatile("isync\n"
-				     PPC_SLBIA(1) "\n"
+			asm volatile(PPC_SLBIA(1) "\n"
 				     "slbmte	%0,%1\n"
 				     "isync"
 				     :: "r"(ksp_vsid_data),
@@ -466,6 +470,13 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
 
 		slb_allocate_user(mm, ea);
 	}
+
+	/*
+	 * Synchronize slbmte preloads with possible subsequent user memory
+	 * address accesses by the kernel (user mode won't happen until
+	 * rfid, which is safe).
+	 */
+	asm volatile("isync" : : : "memory");
 }
 
 void slb_set_size(u16 size)
@@ -609,6 +620,17 @@ static long slb_insert_entry(unsigned long ea, unsigned long context,
 	if (!vsid)
 		return -EFAULT;
 
+	/*
+	 * There must not be a kernel SLB fault in alloc_slb_index or before
+	 * slbmte here or the allocation bitmaps could get out of whack with
+	 * the SLB.
+	 *
+	 * User SLB faults or preloads take this path which might get inlined
+	 * into the caller, so add compiler barriers here to ensure unsafe
+	 * memory accesses do not come between
+	 */
+	barrier();
+
 	index = alloc_slb_index(kernel);
 
 	vsid_data = __mk_vsid_data(vsid, ssize, flags);
@@ -617,10 +639,13 @@ static long slb_insert_entry(unsigned long ea, unsigned long context,
 	/*
 	 * No need for an isync before or after this slbmte. The exception
 	 * we enter with and the rfid we exit with are context synchronizing.
-	 * Also we only handle user segments here.
+	 * User preloads should add isync afterwards in case the kernel
+	 * accesses user memory before it returns to userspace with rfid.
 	 */
 	asm volatile("slbmte %0, %1" : : "r" (vsid_data), "r" (esid_data));
 
+	barrier();
+
 	if (!kernel)
 		slb_cache_update(esid_data);
 
Signature · 1 line
2.18.0
author reviewer
#6 lore raw

PATCH v2 6/9 powerpc/64s/hash: SLB fix preemption issues

In reply to Nicholas Piggin

slb_setup_new_exec and preload_new_slb_context run with interrupts and preemption enabled, which can be corrupted by re-entrant interrupt or process touching SLB preload cache or SLB allocator.

Hard disable interrupts over these regions.

Fixes 5e46e29e6a97 ("powerpc/64s/hash: convert SLB miss handlers to C")
Fixes 89ca4e126a3f ("powerpc/64s/hash: Add a SLB preload cache")
---
 arch/powerpc/mm/slb.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/powerpc/mm/slb.c +12 -0
index 8c38659f1b6b..b5a33da875b5 100644
@@ -311,6 +311,8 @@ void slb_setup_new_exec(void)
 	struct mm_struct *mm = current->mm;
 	unsigned long exec = 0x10000000;
 
+	WARN_ON(irqs_disabled());
+
 	/*
 	 * preload cache can only be used to determine whether a SLB
 	 * entry exists if it does not start to overflow.
@@ -318,6 +320,8 @@ void slb_setup_new_exec(void)
 	if (ti->slb_preload_nr + 2 > SLB_PRELOAD_NR)
 		return;
 
+	hard_irq_disable();
+
 	/*
 	 * We have no good place to clear the slb preload cache on exec,
 	 * flush_thread is about the earliest arch hook but that happens
@@ -347,6 +351,8 @@ void slb_setup_new_exec(void)
 
 	/* see switch_slb */
 	asm volatile("isync" : : : "memory");
+
+	local_irq_enable();
 }
 
 void preload_new_slb_context(unsigned long start, unsigned long sp)
@@ -355,10 +361,14 @@ void preload_new_slb_context(unsigned long start, unsigned long sp)
 	struct mm_struct *mm = current->mm;
 	unsigned long heap = mm->start_brk;
 
+	WARN_ON(irqs_disabled());
+
 	/* see above */
 	if (ti->slb_preload_nr + 3 > SLB_PRELOAD_NR)
 		return;
 
+	hard_irq_disable();
+
 	/* Userspace entry address. */
 	if (!is_kernel_addr(start)) {
 		if (preload_add(ti, start))
@@ -379,6 +389,8 @@ void preload_new_slb_context(unsigned long start, unsigned long sp)
 
 	/* see switch_slb */
 	asm volatile("isync" : : : "memory");
+
+	local_irq_enable();
 }
 
 
Signature · 1 line
2.18.0
author reviewer
#7 lore raw

PATCH v2 7/9 powerpc/64s/hash: Fix false positive preempt warnings

In reply to Nicholas Piggin
Fixes 5e46e29e6a97 ("powerpc/64s/hash: convert SLB miss handlers to C")
---
 arch/powerpc/mm/slb.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)
arch/powerpc/mm/slb.c +12 -12
index b5a33da875b5..c273d0baf4fc 100644
@@ -558,21 +558,21 @@ static void slb_cache_update(unsigned long esid_data)
 	/*
 	 * Now update slb cache entries
 	 */
-	slb_cache_index = get_paca()->slb_cache_ptr;
+	slb_cache_index = local_paca->slb_cache_ptr;
 	if (slb_cache_index < SLB_CACHE_ENTRIES) {
 		/*
 		 * We have space in slb cache for optimized switch_slb().
 		 * Top 36 bits from esid_data as per ISA
 		 */
-		get_paca()->slb_cache[slb_cache_index++] = esid_data >> 28;
-		get_paca()->slb_cache_ptr++;
+		local_paca->slb_cache[slb_cache_index++] = esid_data >> 28;
+		local_paca->slb_cache_ptr++;
 	} else {
 		/*
 		 * Our cache is full and the current cache content strictly
 		 * doesn't indicate the active SLB conents. Bump the ptr
 		 * so that switch_slb() will ignore the cache.
 		 */
-		get_paca()->slb_cache_ptr = SLB_CACHE_ENTRIES + 1;
+		local_paca->slb_cache_ptr = SLB_CACHE_ENTRIES + 1;
 	}
 }
 
@@ -596,24 +596,24 @@ static enum slb_index alloc_slb_index(bool kernel)
 	 * POWER7/8/9 have 32 SLB entries, this could be expanded if a
 	 * future CPU has more.
 	 */
-	if (get_paca()->slb_used_bitmap != U32_MAX) {
-		index = ffz(get_paca()->slb_used_bitmap);
-		get_paca()->slb_used_bitmap |= 1U << index;
+	if (local_paca->slb_used_bitmap != U32_MAX) {
+		index = ffz(local_paca->slb_used_bitmap);
+		local_paca->slb_used_bitmap |= 1U << index;
 		if (kernel)
-			get_paca()->slb_kern_bitmap |= 1U << index;
+			local_paca->slb_kern_bitmap |= 1U << index;
 	} else {
 		/* round-robin replacement of slb starting at SLB_NUM_BOLTED. */
-		index = get_paca()->stab_rr;
+		index = local_paca->stab_rr;
 		if (index < (mmu_slb_size - 1))
 			index++;
 		else
 			index = SLB_NUM_BOLTED;
-		get_paca()->stab_rr = index;
+		local_paca->stab_rr = index;
 		if (index < 32) {
 			if (kernel)
-				get_paca()->slb_kern_bitmap |= 1U << index;
+				local_paca->slb_kern_bitmap |= 1U << index;
 			else
-				get_paca()->slb_kern_bitmap &= ~(1U << index);
+				local_paca->slb_kern_bitmap &= ~(1U << index);
 		}
 	}
 	BUG_ON(index < SLB_NUM_BOLTED);
Signature · 1 line
2.18.0
author reviewer
#8 lore raw

PATCH v2 8/9 powerpc/64s/hash: slb_flush_and_rebolt simplify

In reply to Nicholas Piggin

slb_flush_and_rebolt is misleading, it is called in virtual mode, so it can not possibly change the stack, so it should not be touching the shadow area. And since vmalloc is no longer bolted, it should not change any bolted mappings at all.

Change the name to slb_flush_and_restore_bolted, and have it just load the kernel stack from what's currently in the shadow SLB area.

---
 arch/powerpc/include/asm/book3s/64/mmu-hash.h |  2 +-
 arch/powerpc/kernel/swsusp_asm64.S            |  2 +-
 arch/powerpc/mm/hash_utils_64.c               |  4 +-
 arch/powerpc/mm/slb.c                         | 46 +++++++------------
 arch/powerpc/mm/slice.c                       |  2 +-
 5 files changed, 21 insertions(+), 35 deletions(-)
arch/powerpc/include/asm/book3s/64/mmu-hash.h +1 -1
index 14e552ea5e52..60cda8fb0677 100644
@@ -503,7 +503,7 @@ struct slb_entry {
 };
 
 extern void slb_initialize(void);
-extern void slb_flush_and_rebolt(void);
+void slb_flush_and_restore_bolted(void);
 void slb_flush_all_realmode(void);
 void __slb_restore_bolted_realmode(void);
 void slb_restore_bolted_realmode(void);
arch/powerpc/kernel/swsusp_asm64.S +1 -1
index f83bf6f72cb0..185216becb8b 100644
@@ -262,7 +262,7 @@ END_FW_FTR_SECTION_IFCLR(FW_FEATURE_LPAR)
 
 	addi	r1,r1,-128
 #ifdef CONFIG_PPC_BOOK3S_64
-	bl	slb_flush_and_rebolt
+	bl	slb_flush_and_restore_bolted
 #endif
 	bl	do_after_copyback
 	addi	r1,r1,128
arch/powerpc/mm/hash_utils_64.c +2 -2
index f23a89d8e4ce..974d0332e1d8 100644
@@ -1125,7 +1125,7 @@ void demote_segment_4k(struct mm_struct *mm, unsigned long addr)
 	if ((get_paca_psize(addr) != MMU_PAGE_4K) && (current->mm == mm)) {
 
 		copy_mm_to_paca(mm);
-		slb_flush_and_rebolt();
+		slb_flush_and_restore_bolted();
 	}
 }
 #endif /* CONFIG_PPC_64K_PAGES */
@@ -1197,7 +1197,7 @@ static void check_paca_psize(unsigned long ea, struct mm_struct *mm,
 	if (user_region) {
 		if (psize != get_paca_psize(ea)) {
 			copy_mm_to_paca(mm);
-			slb_flush_and_rebolt();
+			slb_flush_and_restore_bolted();
 		}
 	} else if (get_paca()->vmalloc_sllp !=
 		   mmu_psize_defs[mmu_vmalloc_psize].sllp) {
arch/powerpc/mm/slb.c +16 -30

73 lines not on this page · raw message

arch/powerpc/mm/slice.c +1 -1
index 546dd07c8083..50ba3d0456a5 100644
@@ -219,7 +219,7 @@ static void slice_flush_segments(void *parm)
 	copy_mm_to_paca(current->active_mm);
 
 	local_irq_save(flags);
-	slb_flush_and_rebolt();
+	slb_flush_and_restore_bolted();
 	local_irq_restore(flags);
 #endif
 }
Signature · 1 line
2.18.0
author reviewer
#9 lore raw

PATCH v2 9/9 powerpc/64s/hash: add some slb debugging tests

In reply to Nicholas Piggin

This adds CONFIG_DEBUG_VM checks to ensure:
- The kernel stack is in the SLB after it's flushed and bolted.
- We don't insert an SLB for an address that is aleady in the SLB.
- The kernel SLB miss handler does not take an SLB miss.

---
 arch/powerpc/include/asm/paca.h |  3 ++
 arch/powerpc/mm/slb.c           | 53 +++++++++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 3 deletions(-)
arch/powerpc/include/asm/paca.h +3 -0
index 4838149ee07b..2bfbd8811b72 100644
@@ -115,6 +115,9 @@ struct paca_struct {
 	u16 vmalloc_sllp;
 	u8 slb_cache_ptr;
 	u8 stab_rr;			/* stab/slb round-robin counter */
+#ifdef CONFIG_DEBUG_VM
+	u8 in_kernel_slb_handler;
+#endif
 	u32 slb_used_bitmap;		/* Bitmaps for first 32 SLB entries. */
 	u32 slb_kern_bitmap;
 	u32 slb_cache[SLB_CACHE_ENTRIES];
arch/powerpc/mm/slb.c +50 -3

96 lines not on this page · raw message

Signature · 1 line
2.18.0