LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC/PATCH 5/7] powerpc: Enable lazy save VMX registers for SMP
From: Michael Neuling @ 2010-12-06 23:40 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <20101206234043.083045003@neuling.org>

This enables lazy save of VMX registers for SMP configurations.

This adds a pointer to the thread struct to say which CPU holds this
processes VMX register state.  On 64 bit, this points to the paca of
the CPU holding the state or NULL if it's in the thread_struct.  On 32
bit, this is the CPU number of the CPU holding the state or -1 if it's
in the thread_struct.

It also adds a per cpu pointer (paca on 64bit), which points to the
thread_struct of the process who's state we currently own. 

On a context switch we do the following:
 - if we are switching to a CPU that currently holds the new processes
   state, just turn on VMX in the MSR (this is the lazy/quick case)
 - if the new processes state is in the thread_struct, turn VMX off.
 - if the new processes state is in someone else's CPU, IPI that CPU
   to giveup it's state and turn VMX off.
We always start the new process at this point, irrespective of if we
have the state or not in the thread struct or current CPU.

When we take the VMX unavailable, load_up_altivec checks to see if the
state is now in the thread_struct.  If it is, we restore the VMX
registers and start the process.  If it's not, we need to wait for the
IPI to finish.  Unfortunately, IRQs are off on the current CPU at this
point, so we must turn IRQs on (to avoid a deadlock) before we block
waiting for the IPI to finished on the other CPU.

We also change load_up_altivec to call giveup_altivec to save it's
state rather than duplicating this code.  This means that
giveup_altivec can now be called with the MMU on or off, hence we pass
in an offset, which gets subtracted on 32 bit systems on loads and
stores. 

For 32bit it's be nice to have last_used_altivec cacheline aligned or
as per_cpu variables but we can't access per_cpu vars in asm

Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/paca.h              |    5 
 arch/powerpc/include/asm/processor.h         |   22 ++
 arch/powerpc/include/asm/reg.h               |    9 +
 arch/powerpc/include/asm/system.h            |   10 -
 arch/powerpc/kernel/asm-offsets.c            |    4 
 arch/powerpc/kernel/paca.c                   |    3 
 arch/powerpc/kernel/process.c                |  138 ++++++++++++------
 arch/powerpc/kernel/vector.S                 |  199 ++++++++++++++++++++-------
 arch/powerpc/platforms/pseries/hotplug-cpu.c |    1 
 9 files changed, 288 insertions(+), 103 deletions(-)

Index: linux-lazy/arch/powerpc/include/asm/paca.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/paca.h
+++ linux-lazy/arch/powerpc/include/asm/paca.h
@@ -145,6 +145,11 @@
 	u64 dtl_ridx;			/* read index in dispatch log */
 	struct dtl_entry *dtl_curr;	/* pointer corresponding to dtl_ridx */
 
+#ifdef CONFIG_ALTIVEC
+	/* lazy save pointers */
+	struct task_struct *last_used_altivec;
+#endif
+
 #ifdef CONFIG_KVM_BOOK3S_HANDLER
 	/* We use this to store guest state in */
 	struct kvmppc_book3s_shadow_vcpu shadow_vcpu;
Index: linux-lazy/arch/powerpc/include/asm/processor.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/processor.h
+++ linux-lazy/arch/powerpc/include/asm/processor.h
@@ -18,6 +18,16 @@
 #define TS_FPRWIDTH 1
 #endif
 
+#ifdef CONFIG_PPC64
+#ifdef __ASSEMBLY__
+#define TS_LAZY_STATE_INVALID 0
+#else
+#define TS_LAZY_STATE_INVALID NULL
+#endif
+#else
+#define TS_LAZY_STATE_INVALID -1
+#endif
+
 #ifndef __ASSEMBLY__
 #include <linux/compiler.h>
 
@@ -48,6 +58,7 @@
 
 #define spin_lock_prefetch(x)	prefetchw(x)
 
+#include <linux/call_single_data.h>
 #include <asm/ptrace.h>
 #include <asm/types.h>
 
@@ -109,7 +120,6 @@
 
 /* Lazy FPU handling on uni-processor */
 extern struct task_struct *last_task_used_math;
-extern struct task_struct *last_task_used_altivec;
 extern struct task_struct *last_task_used_vsx;
 extern struct task_struct *last_task_used_spe;
 
@@ -177,6 +187,14 @@
 #define TS_VSRLOWOFFSET 1
 #define TS_FPR(i) fpr[i][TS_FPROFFSET]
 
+#ifdef CONFIG_PPC64
+#define TS_LAZY_STATE_TYPE struct paca_struct *
+#define TS_LAZY_STATE_INVALID NULL
+#else
+#define TS_LAZY_STATE_TYPE unsigned long
+#define TS_LAZY_STATE_INVALID -1
+#endif
+
 struct thread_struct {
 	unsigned long	ksp;		/* Kernel stack pointer */
 	unsigned long	ksp_limit;	/* if ksp <= ksp_limit stack overflow */
@@ -253,6 +271,8 @@
 	/* AltiVec status */
 	vector128	vscr __attribute__((aligned(16)));
 	unsigned long	vrsave;
+	TS_LAZY_STATE_TYPE vr_state;	/* where my vr state is */
+	struct call_single_data vr_csd;	/* IPI data structure */
 	int		used_vr;	/* set if process has used altivec */
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
Index: linux-lazy/arch/powerpc/include/asm/reg.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/reg.h
+++ linux-lazy/arch/powerpc/include/asm/reg.h
@@ -808,6 +808,15 @@
 
 #define __is_processor(pv)	(PVR_VER(mfspr(SPRN_PVR)) == (pv))
 
+#ifdef CONFIG_PPC64
+#define GET_CURRENT_THREAD(reg)			\
+	ld	(reg),PACACURRENT(r13) ;	\
+	addi    (reg),(reg),THREAD
+#else
+#define GET_CURRENT_THREAD(reg)			\
+	mfspr	(reg),SPRN_SPRG_THREAD
+#endif
+
 /*
  * IBM has further subdivided the standard PowerPC 16-bit version and
  * revision subfields of the PVR for the PowerPC 403s into the following:
Index: linux-lazy/arch/powerpc/include/asm/system.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/system.h
+++ linux-lazy/arch/powerpc/include/asm/system.h
@@ -7,6 +7,7 @@
 #include <linux/kernel.h>
 #include <linux/irqflags.h>
 
+#include <asm/irqflags.h>
 #include <asm/hw_irq.h>
 
 /*
@@ -145,7 +146,8 @@
 extern void enable_kernel_fp(void);
 extern void flush_fp_to_thread(struct task_struct *);
 extern void enable_kernel_altivec(void);
-extern void giveup_altivec(struct task_struct *);
+extern void giveup_altivec(unsigned long offset);
+extern void giveup_altivec_ipi(void *);
 extern void load_up_altivec(struct task_struct *);
 extern int emulate_altivec(struct pt_regs *);
 extern void __giveup_vsx(struct task_struct *);
@@ -157,13 +159,7 @@
 extern void cvt_fd(float *from, double *to);
 extern void cvt_df(double *from, float *to);
 
-#ifndef CONFIG_SMP
 extern void discard_lazy_cpu_state(void);
-#else
-static inline void discard_lazy_cpu_state(void)
-{
-}
-#endif
 
 #ifdef CONFIG_ALTIVEC
 extern void flush_altivec_to_thread(struct task_struct *);
Index: linux-lazy/arch/powerpc/kernel/asm-offsets.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/asm-offsets.c
+++ linux-lazy/arch/powerpc/kernel/asm-offsets.c
@@ -89,6 +89,7 @@
 	DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave));
 	DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr));
 	DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr));
+	DEFINE(THREAD_VR_STATE, offsetof(struct thread_struct, vr_state));
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
 	DEFINE(THREAD_VSR0, offsetof(struct thread_struct, fpr));
@@ -197,6 +198,9 @@
 	DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time));
 	DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time));
 	DEFINE(PACA_TRAP_SAVE, offsetof(struct paca_struct, trap_save));
+#ifdef CONFIG_ALTIVEC
+	DEFINE(PACA_LAST_USED_ALTIVEC, offsetof(struct paca_struct, last_used_altivec));
+#endif
 #ifdef CONFIG_KVM_BOOK3S_64_HANDLER
 	DEFINE(PACA_KVM_SVCPU, offsetof(struct paca_struct, shadow_vcpu));
 	DEFINE(SVCPU_SLB, offsetof(struct kvmppc_book3s_shadow_vcpu, slb));
Index: linux-lazy/arch/powerpc/kernel/paca.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/paca.c
+++ linux-lazy/arch/powerpc/kernel/paca.c
@@ -162,6 +162,9 @@
 	new_paca->hw_cpu_id = 0xffff;
 	new_paca->kexec_state = KEXEC_STATE_NONE;
 	new_paca->__current = &init_task;
+#ifdef CONFIG_ALTIVEC
+	new_paca->last_used_altivec = NULL;
+#endif
 #ifdef CONFIG_PPC_STD_MMU_64
 	new_paca->slb_shadow_ptr = &slb_shadow[cpu];
 #endif /* CONFIG_PPC_STD_MMU_64 */
Index: linux-lazy/arch/powerpc/kernel/process.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/process.c
+++ linux-lazy/arch/powerpc/kernel/process.c
@@ -59,7 +59,6 @@
 
 #ifndef CONFIG_SMP
 struct task_struct *last_task_used_math = NULL;
-struct task_struct *last_task_used_altivec = NULL;
 struct task_struct *last_task_used_vsx = NULL;
 struct task_struct *last_task_used_spe = NULL;
 #endif
@@ -117,14 +116,7 @@
 {
 	WARN_ON(preemptible());
 
-#ifdef CONFIG_SMP
-	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC))
-		giveup_altivec(current);
-	else
-		giveup_altivec(NULL);	/* just enable AltiVec for kernel - force */
-#else
-	giveup_altivec(last_task_used_altivec);
-#endif /* CONFIG_SMP */
+	giveup_altivec(0);
 }
 EXPORT_SYMBOL(enable_kernel_altivec);
 
@@ -134,16 +126,7 @@
  */
 void flush_altivec_to_thread(struct task_struct *tsk)
 {
-	if (tsk->thread.regs) {
-		preempt_disable();
-		if (tsk->thread.regs->msr & MSR_VEC) {
-#ifdef CONFIG_SMP
-			BUG_ON(tsk != current);
-#endif
-			giveup_altivec(tsk);
-		}
-		preempt_enable();
-	}
+	giveup_altivec(0);
 }
 #endif /* CONFIG_ALTIVEC */
 
@@ -169,7 +152,7 @@
 void giveup_vsx(struct task_struct *tsk)
 {
 	giveup_fpu(tsk);
-	giveup_altivec(tsk);
+	giveup_altivec(0);
 	__giveup_vsx(tsk);
 }
 
@@ -220,7 +203,6 @@
 }
 #endif /* CONFIG_SPE */
 
-#ifndef CONFIG_SMP
 /*
  * If we are doing lazy switching of CPU state (FP, altivec or SPE),
  * and the current task has some state, discard it.
@@ -228,12 +210,12 @@
 void discard_lazy_cpu_state(void)
 {
 	preempt_disable();
-	if (last_task_used_math == current)
-		last_task_used_math = NULL;
 #ifdef CONFIG_ALTIVEC
-	if (last_task_used_altivec == current)
-		last_task_used_altivec = NULL;
+	giveup_altivec(0);
 #endif /* CONFIG_ALTIVEC */
+#ifndef CONFIG_SMP
+	if (last_task_used_math == current)
+		last_task_used_math = NULL;
 #ifdef CONFIG_VSX
 	if (last_task_used_vsx == current)
 		last_task_used_vsx = NULL;
@@ -242,9 +224,9 @@
 	if (last_task_used_spe == current)
 		last_task_used_spe = NULL;
 #endif
+#endif /* CONFIG_SMP */
 	preempt_enable();
 }
-#endif /* CONFIG_SMP */
 
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
 void do_send_trap(struct pt_regs *regs, unsigned long address,
@@ -386,6 +368,78 @@
 #ifdef CONFIG_PPC64
 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
 #endif
+#ifdef CONFIG_PPC64
+#define LAZY_STATE_HERE (get_paca())
+#define LAZY_STATE_CPU_ID (state->hw_cpu_id)
+#else
+#define LAZY_STATE_HERE (smp_processor_id())
+#define LAZY_STATE_CPU_ID (state)
+#endif
+
+extern int csd_locked(struct call_single_data *data);
+
+#ifdef CONFIG_ALTIVEC
+/* Return value indicates if it was lazy or not */
+static bool switch_to_altivec_lazy(struct task_struct *prev,
+				   struct task_struct *new)
+{
+	/*
+	 * At this point the Altivec reg state can be in 1 of 3 places
+	 * 1) cached on _this_ CPU.   Lazy/fast  :-)
+	 * 2) in the thread_struct.   Normal     :-|
+	 * 3) cached on another CPU.  Slow IPI   :-(
+         * .... lets go workout what happened....
+	 */
+
+	/* Cache the state pointer here incase it changes */
+	TS_LAZY_STATE_TYPE state = new->thread.vr_state;
+
+	/* Is the state here? */
+	if (state == LAZY_STATE_HERE) {
+		/* It's here! Excellent, simply turn VMX on */
+		new->thread.regs->msr |= MSR_VEC;
+		return true;
+	}
+	/*
+	 * If we have used VMX in the past, but don't have lazy state,
+	 * then make sure we turn off VMX.  load_up_altivec will deal
+	 * with saving the lazy state if we run a VMX instruction
+	 */
+	new->thread.regs->msr &= ~MSR_VEC;
+
+	if (state != TS_LAZY_STATE_INVALID) {
+#ifdef CONFIG_SMP
+		/*
+		 * To avoid a deadlock, make sure we don't
+		 * have someone else state here
+		 */
+		discard_lazy_cpu_state();
+
+		/*
+		 * Get the other CPU to flush it's state
+		 * synchronously.  It's possible this may may get run
+		 * multiple times, but giveup_altivec can handle this.
+		 */
+		if (!csd_locked(&(new->thread.vr_csd)))
+			__smp_call_function_single(
+				LAZY_STATE_CPU_ID,
+				&(new->thread.vr_csd),
+				0);
+#else /* CONFIG_SMP */
+		/* UP can't have state on another CPU */
+		BUG();
+#endif
+
+	}
+	return false;
+}
+#else /* CONFIG_ALTIVEC */
+static bool switch_to_altivec_lazy(struct task_struct *prev,
+				  struct task_struct *new)
+{
+	return true;
+}
+#endif /* CONFIG_ALTIVEC */
 
 struct task_struct *__switch_to(struct task_struct *prev,
 	struct task_struct *new)
@@ -393,6 +447,12 @@
 	struct thread_struct *new_thread, *old_thread;
 	unsigned long flags;
 	struct task_struct *last;
+	int lazy = 1;
+
+	/* Does next have lazy state somewhere? */
+	if (new->thread.regs) {
+		lazy &= switch_to_altivec_lazy(prev, new);
+	}
 
 #ifdef CONFIG_SMP
 	/* avoid complexity of lazy save/restore of fpu
@@ -406,21 +466,6 @@
 	 */
 	if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP))
 		giveup_fpu(prev);
-#ifdef CONFIG_ALTIVEC
-	/*
-	 * If the previous thread used altivec in the last quantum
-	 * (thus changing altivec regs) then save them.
-	 * We used to check the VRSAVE register but not all apps
-	 * set it, so we don't rely on it now (and in fact we need
-	 * to save & restore VSCR even if VRSAVE == 0).  -- paulus
-	 *
-	 * On SMP we always save/restore altivec regs just to avoid the
-	 * complexity of changing processors.
-	 *  -- Cort
-	 */
-	if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC))
-		giveup_altivec(prev);
-#endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
 	if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX))
 		/* VMX and FPU registers are already save here */
@@ -439,13 +484,6 @@
 #endif /* CONFIG_SPE */
 
 #else  /* CONFIG_SMP */
-#ifdef CONFIG_ALTIVEC
-	/* Avoid the trap.  On smp this this never happens since
-	 * we don't set last_task_used_altivec -- Cort
-	 */
-	if (new->thread.regs && last_task_used_altivec == new)
-		new->thread.regs->msr |= MSR_VEC;
-#endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
 	if (new->thread.regs && last_task_used_vsx == new)
 		new->thread.regs->msr |= MSR_VSX;
@@ -862,6 +900,10 @@
 	current->thread.vscr.u[3] = 0x00010000; /* Java mode disabled */
 	current->thread.vrsave = 0;
 	current->thread.used_vr = 0;
+	current->thread.vr_state = TS_LAZY_STATE_INVALID;
+	current->thread.vr_csd.func = giveup_altivec_ipi;
+	current->thread.vr_csd.info = 0;
+	current->thread.vr_csd.flags = 0;
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_SPE
 	memset(current->thread.evr, 0, sizeof(current->thread.evr));
Index: linux-lazy/arch/powerpc/kernel/vector.S
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/vector.S
+++ linux-lazy/arch/powerpc/kernel/vector.S
@@ -5,7 +5,29 @@
 #include <asm/cputable.h>
 #include <asm/thread_info.h>
 #include <asm/page.h>
+#include <asm/exception-64s.h>
+#include <linux/threads.h>
 
+#ifdef CONFIG_PPC32
+	.section .bss
+	.align	4
+last_used_altivec:
+	.space	4*NR_CPUS
+	.previous
+/*
+ * Get the last_used_altivec pointer for this cpu.
+ * Pointer ends up in register n.  offset in a, volotile scratch in b
+ */
+#define LAST_USED_ALTIVEC_PTR(n, a, b)		\
+	rlwinm	b,r1,0,0,(31-THREAD_SHIFT) ;	\
+        sub     b,b,a	;			\
+	lwz	b,TI_CPU(b) ;			\
+	slwi	b,b,2	    ;			\
+	lis	n,last_used_altivec@ha ;	\
+	addi	n,n,last_used_altivec@l	;	\
+	sub	n,n,b			;	\
+	add	n,n,b
+#endif
 /*
  * load_up_altivec(unused, unused, tsk)
  * Disable VMX for the task which had it previously,
@@ -20,38 +42,98 @@
 	MTMSRD(r5)			/* enable use of AltiVec now */
 	isync
 
+	mflr	r10
+#ifdef CONFIG_PPC32
+	lis	r3, PAGE_OFFSET@h
+#endif
+	bl	giveup_altivec_msr_done
 /*
- * For SMP, we don't do lazy VMX switching because it just gets too
- * horrendously complex, especially when a task switches from one CPU
- * to another.  Instead we call giveup_altvec in switch_to.
- * VRSAVE isn't dealt with here, that is done in the normal context
- * switch code. Note that we could rely on vrsave value to eventually
- * avoid saving all of the VREGs here...
+ * lazy restore:
+ * 	If we are doing lazy restore we enter here either:
+ * 	1. never done vmx before
+ * 	2. done vmx and state is in our thread_struct
+ * 	3. done vmx and but state is being flushed via an IPI
  */
-#ifndef CONFIG_SMP
-	LOAD_REG_ADDRBASE(r3, last_task_used_altivec)
-	toreal(r3)
-	PPC_LL	r4,ADDROFF(last_task_used_altivec)(r3)
-	PPC_LCMPI	0,r4,0
-	beq	1f
+	GET_CURRENT_THREAD(r5)
+	lwz 	r4,THREAD_USED_VR(r5)
+	PPC_LCMPI	cr0,r4,0 /* we've not used vmx before */
+	beq	4f
+
+	/*
+         * Spin here waiting for IPI to finish.  Once the data is in
+	 * our thread_struct, vr_state will be null:
+	 *
+	 * First quickly check to see if data has been flushed from
+	 * another CPU yet (as it's likely the IPI has completed)
+	 */
+5:
+	PPC_LL	r4,THREAD_VR_STATE(r5)
+	PPC_LCMPI	0,r4,TS_LAZY_STATE_INVALID
+	beq+	3f /* it's likely the data is already here */
+	/*
+	* Bugger, the IPI has not completed.  Let's spin here waiting
+	* for it, but we should turn on IRQ incase someone is wait for
+	* us for something.
+	 */
 
-	/* Save VMX state to last_task_used_altivec's THREAD struct */
-	toreal(r4)
-	addi	r4,r4,THREAD
-	SAVE_32VRS(0,r5,r4)
-	mfvscr	vr0
-	li	r10,THREAD_VSCR
-	stvx	vr0,r10,r4
-	/* Disable VMX for last_task_used_altivec */
-	PPC_LL	r5,PT_REGS(r4)
-	toreal(r5)
-	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
-	lis	r10,MSR_VEC@h
-	andc	r4,r4,r10
-	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
-1:
-#endif /* CONFIG_SMP */
+	/* Enable IRQs */
+#ifdef CONFIG_PPC32
+	mfmsr	r4
+	rlwimi	r4,r9,0,MSR_EE
+	MTMSRD(r4)
+#else
+	ENABLE_INTS
+#endif
+2:
+	/* Wait for lazy state to appear */
+	PPC_LL  r4,THREAD_VR_STATE(r5)
+	PPC_LCMPI	0,r4,TS_LAZY_STATE_INVALID
+	bne     2b
 
+	/* disable irqs and enable vec again */
+#ifdef CONFIG_PPC32
+	mfmsr	r4
+	oris	r4,r4,MSR_VEC@h
+	xori	r4,r4,MSR_EE
+	MTMSRD(r4)
+#else
+	mfmsr   r11
+	oris    r11,r11,MSR_VEC@h
+	xori	r11,r11,MSR_EE
+	MTMSRD(r11)
+#endif
+	/*
+	 * make sure we didn't pickup someones state while we had IRQs
+	 * on
+	 */
+#ifdef CONFIG_PPC32
+	lis	r3, PAGE_OFFSET@h
+#endif
+        bl      giveup_altivec_msr_done
+3:
+	LWSYNC /* make sure VMX registers are in memory */
+4:
+	mtlr	r10
+	/* setup lazy pointers */
+	GET_CURRENT_THREAD(r5)
+#ifdef CONFIG_PPC64
+	PPC_STL	r13,THREAD_VR_STATE(r5)
+#else
+	/* get the cpuid */
+	lis	r6,PAGE_OFFSET@h
+	rlwinm  r7,r1,0,0,(31-THREAD_SHIFT)
+	sub     r7,r7,r6
+	lwz     r7,TI_CPU(r7)
+	PPC_STL	r7,THREAD_VR_STATE(r5) /* write the cpuid */
+#endif
+	subi	r4, r5, THREAD
+#ifdef CONFIG_PPC64
+	PPC_STL	r4,PACA_LAST_USED_ALTIVEC(r13)
+#else
+/*	lis	r6,PAGE_OFFSET@h */
+	LAST_USED_ALTIVEC_PTR(r3, r6, r7)
+	PPC_STL	r4,0(r3)
+#endif
 	/* Hack: if we get an altivec unavailable trap with VRSAVE
 	 * set to all zeros, we assume this is a broken application
 	 * that fails to set it properly, and thus we switch it to
@@ -65,11 +147,8 @@
 1:
 	/* enable use of VMX after return */
 #ifdef CONFIG_PPC32
-	mfspr	r5,SPRN_SPRG_THREAD		/* current task's THREAD (phys) */
 	oris	r9,r9,MSR_VEC@h
 #else
-	ld	r4,PACACURRENT(r13)
-	addi	r5,r4,THREAD		/* Get THREAD */
 	oris	r12,r12,MSR_VEC@h
 	std	r12,_MSR(r1)
 #endif
@@ -79,29 +158,41 @@
 	lvx	vr0,r10,r5
 	mtvscr	vr0
 	REST_32VRS(0,r4,r5)
-#ifndef CONFIG_SMP
-	/* Update last_task_used_altivec to 'current' */
-	subi	r4,r5,THREAD		/* Back to 'current' */
-	fromreal(r4)
-	PPC_STL	r4,ADDROFF(last_task_used_altivec)(r3)
-#endif /* CONFIG_SMP */
 	/* restore registers and return */
 	blr
-
 /*
- * giveup_altivec(tsk)
- * Disable VMX for the task given as the argument,
- * and save the vector registers in its thread_struct.
+ * giveup_altivec(offset)
+ * Disable VMX for the task currently using vmx and and save the
+ * vector registers in its thread_struct.
  * Enables the VMX for use in the kernel on return.
  */
+_GLOBAL(giveup_altivec_ipi)
 _GLOBAL(giveup_altivec)
 	mfmsr	r5
 	oris	r5,r5,MSR_VEC@h
 	SYNC
 	MTMSRD(r5)			/* enable use of VMX now */
 	isync
+
+giveup_altivec_msr_done:
+#ifdef CONFIG_PPC64
+	PPC_LL	r3,PACA_LAST_USED_ALTIVEC(r13)
+#else
+	mr	r7, r3
+	LAST_USED_ALTIVEC_PTR(r4, r7, r5)
+	PPC_LL	r3,0(r4) /* phys address */
+#endif
 	PPC_LCMPI	0,r3,0
-	beqlr-				/* if no previous owner, done */
+	beqlr				/* if no previous owner, done */
+#ifdef CONFIG_PPC32
+	/* turn phys address into phys or virt based on offset */
+	lis	r6,PAGE_OFFSET@h
+	sub	r6, r6, r7
+	add	r3, r3, r6
+#endif
+2:
+	/* Save state to the thread struct */
+	mr	r6,r3
 	addi	r3,r3,THREAD		/* want THREAD of task */
 	PPC_LL	r5,PT_REGS(r3)
 	PPC_LCMPI	0,r5,0
@@ -110,6 +201,9 @@
 	li	r4,THREAD_VSCR
 	stvx	vr0,r4,r3
 	beq	1f
+#ifdef CONFIG_PPC32
+	sub	r5, r5, r7
+#endif
 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 #ifdef CONFIG_VSX
 BEGIN_FTR_SECTION
@@ -120,14 +214,25 @@
 #else
 	lis	r3,MSR_VEC@h
 #endif
-	andc	r4,r4,r3		/* disable FP for previous task */
+	andc	r4,r4,r3		/* disable vmx for previous task */
 	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 1:
-#ifndef CONFIG_SMP
+	/*
+	 * If this is an ipi, make sure state is is commited before we
+	 * clear the lazy state pointers and return.  If a CPU is waiting on
+	 * this data (IPI case) then it won't start until VR_STATE is cleared
+	 */
+	LWSYNC /* make sure registers are in mem before say they are */
+	li	r5,TS_LAZY_STATE_INVALID
+	PPC_STL	r5,THREAD+THREAD_VR_STATE(r6)
 	li	r5,0
-	LOAD_REG_ADDRBASE(r4,last_task_used_altivec)
-	PPC_STL	r5,ADDROFF(last_task_used_altivec)(r4)
-#endif /* CONFIG_SMP */
+#ifdef CONFIG_PPC64
+	PPC_STL	r5,PACA_LAST_USED_ALTIVEC(r13)
+#else
+	LAST_USED_ALTIVEC_PTR(r3, r7, r4)
+	PPC_STL	r5,0(r3)
+#endif
+	LWSYNC
 	blr
 
 #ifdef CONFIG_VSX
Index: linux-lazy/arch/powerpc/platforms/pseries/hotplug-cpu.c
===================================================================
--- linux-lazy.orig/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ linux-lazy/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -112,6 +112,7 @@
 
 	local_irq_disable();
 	idle_task_exit();
+	discard_lazy_cpu_state();
 	xics_teardown_cpu();
 
 	if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) {

^ permalink raw reply

* [RFC/PATCH 6/7] powerpc: Enable lazy save FP registers for SMP
From: Michael Neuling @ 2010-12-06 23:40 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <20101206234043.083045003@neuling.org>

This enables lazy save of FP registers for SMP configurations.

This adds a pointer to the thread struct to say which CPU holds this
processes FP register state.  On 64 bit, this points to the paca of
the CPU holding the state or NULL if it's in the thread_struct.  On 32
bit, this is the CPU number of the CPU holding the state or -1 if it's
in the thread_struct.

It also adds a per cpu pointer (paca on 64bit), which points to the
thread_struct of the process who's state we currently own. 

On a context switch we do the following:
 - if we are switching to a CPU that currently holds the new processes
   state, just turn on FP in the MSR (this is the lazy/quick case)
 - if the new processes state is in the thread_struct, turn FP off.
 - if the new processes state is in someone else's CPU, IPI that CPU
   to giveup it's state and turn FP off.
We always start the new process at this point, irrespective of if we
have the state or not in the thread struct or current CPU.

When we take the FP unavailable, load_up_altivec checks to see if the
state is now in the thread_struct.  If it is, we restore the FP
registers and start the process.  If it's not, we need to wait for the
IPI to finish.  Unfortunately, IRQs are off on the current CPU at this
point, so we must turn IRQs on (to avoid a deadlock) before we block
waiting for the IPI to finished on the other CPU.

We also change load_up_fpu to call giveup_fpu to save it's state
rather than duplicating this code.  This means that giveup_altivec can
now be called with the MMU on or off, hence we pass in an offset,
which gets subtracted on 32 bit systems on loads and stores.

For 32bit it's be nice to have last_used_fp cacheline aligned or as
per_cpu variables but we can't access per_cpu vars in asm.

Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/paca.h      |    1 
 arch/powerpc/include/asm/processor.h |    8 +
 arch/powerpc/include/asm/system.h    |    3 
 arch/powerpc/kernel/asm-offsets.c    |    3 
 arch/powerpc/kernel/fpu.S            |  198 +++++++++++++++++++++++++++--------
 arch/powerpc/kernel/paca.c           |    1 
 arch/powerpc/kernel/process.c        |  114 +++++++++++---------
 7 files changed, 237 insertions(+), 91 deletions(-)

Index: linux-lazy/arch/powerpc/include/asm/paca.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/paca.h
+++ linux-lazy/arch/powerpc/include/asm/paca.h
@@ -145,6 +145,7 @@
 	u64 dtl_ridx;			/* read index in dispatch log */
 	struct dtl_entry *dtl_curr;	/* pointer corresponding to dtl_ridx */
 
+	struct task_struct *last_used_fp;
 #ifdef CONFIG_ALTIVEC
 	/* lazy save pointers */
 	struct task_struct *last_used_altivec;
Index: linux-lazy/arch/powerpc/include/asm/processor.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/processor.h
+++ linux-lazy/arch/powerpc/include/asm/processor.h
@@ -120,7 +120,6 @@
 extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 /* Lazy FPU handling on uni-processor */
-extern struct task_struct *last_task_used_math;
 extern struct task_struct *last_task_used_vsx;
 extern struct task_struct *last_task_used_spe;
 
@@ -253,6 +252,13 @@
 	} fpscr;
 	int		fpexc_mode;	/* floating-point exception mode */
 	unsigned int	align_ctl;	/* alignment handling control */
+	int		used_fp;	/* set if process has used fp */
+#ifdef CONFIG_PPC64
+	struct paca_struct *fp_state;	/* paca where my fp state could be? */
+#else
+	unsigned long	fp_state;	/* paca where my fp state could be? */
+#endif
+	struct call_single_data fp_csd;	/* IPI data structure */
 #ifdef CONFIG_PPC64
 	unsigned long	start_tb;	/* Start purr when proc switched in */
 	unsigned long	accum_tb;	/* Total accumilated purr for process */
Index: linux-lazy/arch/powerpc/include/asm/system.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/system.h
+++ linux-lazy/arch/powerpc/include/asm/system.h
@@ -140,7 +140,8 @@
 extern void via_cuda_init(void);
 extern void read_rtc_time(void);
 extern void pmac_find_display(void);
-extern void giveup_fpu(struct task_struct *);
+extern void giveup_fpu(unsigned long offset);
+extern void giveup_fpu_ipi(void *);
 extern void disable_kernel_fp(void);
 extern void enable_kernel_fp(void);
 extern void flush_fp_to_thread(struct task_struct *);
Index: linux-lazy/arch/powerpc/kernel/asm-offsets.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/asm-offsets.c
+++ linux-lazy/arch/powerpc/kernel/asm-offsets.c
@@ -84,6 +84,8 @@
 	DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode));
 	DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
 	DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr));
+	DEFINE(THREAD_FP_STATE, offsetof(struct thread_struct, fp_state));
+	DEFINE(THREAD_USED_FP, offsetof(struct thread_struct, used_fp));
 #ifdef CONFIG_ALTIVEC
 	DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0]));
 	DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave));
@@ -198,6 +200,7 @@
 	DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time));
 	DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time));
 	DEFINE(PACA_TRAP_SAVE, offsetof(struct paca_struct, trap_save));
+	DEFINE(PACA_LAST_USED_FP, offsetof(struct paca_struct, last_used_fp));
 #ifdef CONFIG_ALTIVEC
 	DEFINE(PACA_LAST_USED_ALTIVEC, offsetof(struct paca_struct, last_used_altivec));
 #endif
Index: linux-lazy/arch/powerpc/kernel/fpu.S
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/fpu.S
+++ linux-lazy/arch/powerpc/kernel/fpu.S
@@ -23,6 +23,8 @@
 #include <asm/thread_info.h>
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
+#include <asm/exception-64s.h>
+#include <linux/threads.h>
 
 #ifdef CONFIG_VSX
 #define REST_32FPVSRS(n,c,base)						\
@@ -47,6 +49,26 @@
 #define SAVE_32FPVSRS(n,b,base)	SAVE_32FPRS(n, base)
 #endif
 
+#ifdef CONFIG_PPC32
+       .section .bss
+       .align  4
+last_used_fp:
+       .space  4*NR_CPUS
+       .previous
+/*
+ * Get the last_used_fp pointer for this cpu.
+ * Pointer ends up in register n.  offset in a, volotile scratch in b
+ */
+#define LAST_USED_FP_PTR(n, a, b)		\
+       rlwinm  b,r1,0,0,(31-THREAD_SHIFT) ;	\
+       sub     b,b,a	;			\
+       lwz     b,TI_CPU(b) ;			\
+       slwi    b,b,2       ;			\
+       lis     n,last_used_fp@ha ;		\
+       addi    n,n,last_used_fp@l ;		\
+       sub     n,n,a	;			\
+       add     n,n,b
+#endif
 /*
  * This task wants to use the FPU now.
  * On UP, disable FP for the task which had the FPU previously,
@@ -65,52 +87,113 @@
 	SYNC
 	MTMSRD(r5)			/* enable use of fpu now */
 	isync
+
+	mflr    r10
+#ifdef CONFIG_PPC32
+	lis	r3, PAGE_OFFSET@h
+#endif
+	bl      giveup_fpu_msr_done
 /*
- * For SMP, we don't do lazy FPU switching because it just gets too
- * horrendously complex, especially when a task switches from one CPU
- * to another.  Instead we call giveup_fpu in switch_to.
+ * lazy restore:
+ * 	If we are doing lazy restore we enter here either:
+ * 	1. never done fp before
+ * 	2. done fp and state is in our thread_struct
+ * 	3. done fp and but state is being flushed via an IPI
  */
-#ifndef CONFIG_SMP
-	LOAD_REG_ADDRBASE(r3, last_task_used_math)
-	toreal(r3)
-	PPC_LL	r4,ADDROFF(last_task_used_math)(r3)
-	PPC_LCMPI	0,r4,0
-	beq	1f
-	toreal(r4)
-	addi	r4,r4,THREAD		/* want last_task_used_math->thread */
-	SAVE_32FPVSRS(0, r5, r4)
-	mffs	fr0
-	stfd	fr0,THREAD_FPSCR(r4)
-	PPC_LL	r5,PT_REGS(r4)
-	toreal(r5)
-	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
-	li	r10,MSR_FP|MSR_FE0|MSR_FE1
-	andc	r4,r4,r10		/* disable FP for previous task */
-	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
-1:
-#endif /* CONFIG_SMP */
+	GET_CURRENT_THREAD(r5)
+	lwz 	r4,THREAD_USED_FP(r5)
+	PPC_LCMPI	cr0,r4,0 /* we've not used fp before */
+	beq	4f
+
+	/*
+         * Spin here waiting for IPI to finish.  Once the data is in
+	 * our thread_struct, cp_state will be null:
+	 *
+	 * First quickly check to see if data has been flushed from
+	 * another CPU yet (as it's likely the IPI has completed)
+	 */
+5:
+	PPC_LL	r4,THREAD_FP_STATE(r5)
+	PPC_LCMPI	0,r4,TS_LAZY_STATE_INVALID
+	beq+	3f /* it's likely the data is already here */
+	/*
+	 * Bugger, the IPI has not completed.  Let's spin here waiting
+	 * for it, but we should turn on IRQ incase someone is wait for
+	 * us for something.
+	 */
+
+	/* Enable IRQs */
+#ifdef CONFIG_PPC32
+	mfmsr	r4
+	rlwimi	r4,r9,0,MSR_EE
+	MTMSRD(r4)
+#else
+	ENABLE_INTS
+#endif
+2:
+	/* Wait for lazy state to appear */
+	PPC_LL	r4,THREAD_FP_STATE(r5)
+	PPC_LCMPI	0,r4,TS_LAZY_STATE_INVALID
+	bne	2b
+
+	/* disable irqs and enable fp again */
+#ifdef CONFIG_PPC32
+	mfmsr	r4
+	ori	r4,r4,MSR_FP
+	xori	r4,r4,MSR_EE
+	MTMSRD(r4)
+#else
+	mfmsr	r11
+	ori	r11,r11,MSR_FP
+	xori	r11,r11,MSR_EE
+	MTMSRD(r11)
+#endif
+	/*
+	 * make sure we didn't pickup someones state while we had IRQs
+	 * on
+	 */
+#ifdef CONFIG_PPC32
+	lis	r3, PAGE_OFFSET@h
+#endif
+	bl	giveup_fpu_msr_done
+3:
+	LWSYNC /* make sure fp registers are in memory */
+4:
+	mtlr	r10
+
+	/* setup lazy pointers */
+	GET_CURRENT_THREAD(r5)
+#ifdef CONFIG_PPC64
+	PPC_STL	r13,THREAD_FP_STATE(r5)
+#else
+	/* get the cpuid */
+	lis	r6,PAGE_OFFSET@h
+	rlwinm  r7,r1,0,0,(31-THREAD_SHIFT)
+	sub     r7,r7,r6
+	lwz     r7,TI_CPU(r7)
+	PPC_STL	r7,THREAD_FP_STATE(r5) /* write the cpuid */
+#endif
+	subi	r4, r5, THREAD
+#ifdef CONFIG_PPC64
+	PPC_STL	r4,PACA_LAST_USED_FP(r13)
+#else
+/*	lis	r6, PAGE_OFFSET@h */
+	LAST_USED_FP_PTR(r3, r6, r7)
+	PPC_STL	r4,0(r3)
+#endif
 	/* enable use of FP after return */
 #ifdef CONFIG_PPC32
-	mfspr	r5,SPRN_SPRG_THREAD		/* current task's THREAD (phys) */
-	lwz	r4,THREAD_FPEXC_MODE(r5)
-	ori	r9,r9,MSR_FP		/* enable FP for current */
-	or	r9,r9,r4
-#else
-	ld	r4,PACACURRENT(r13)
-	addi	r5,r4,THREAD		/* Get THREAD */
-	lwz	r4,THREAD_FPEXC_MODE(r5)
+	ori	r9,r9,MSR_FP
+#else
 	ori	r12,r12,MSR_FP
-	or	r12,r12,r4
 	std	r12,_MSR(r1)
 #endif
+	li	r4,1
+	stw	r4,THREAD_USED_FP(r5)
+	LWSYNC
 	lfd	fr0,THREAD_FPSCR(r5)
 	MTFSF_L(fr0)
 	REST_32FPVSRS(0, r4, r5)
-#ifndef CONFIG_SMP
-	subi	r4,r5,THREAD
-	fromreal(r4)
-	PPC_STL	r4,ADDROFF(last_task_used_math)(r3)
-#endif /* CONFIG_SMP */
 	/* restore registers and return */
 	/* we haven't used ctr or xer or lr */
 	blr
@@ -122,6 +205,7 @@
  * Enables the FPU for use in the kernel on return.
  */
 _GLOBAL(giveup_fpu)
+_GLOBAL(giveup_fpu_ipi)
 	mfmsr	r5
 	ori	r5,r5,MSR_FP
 #ifdef CONFIG_VSX
@@ -134,8 +218,26 @@
 	MTMSRD(r5)			/* enable use of fpu now */
 	SYNC_601
 	isync
+
+giveup_fpu_msr_done:
+#ifdef CONFIG_PPC64
+	PPC_LL	r3,PACA_LAST_USED_FP(r13)
+#else
+	mr	r7, r3
+	LAST_USED_FP_PTR(r4, r7, r5)
+	PPC_LL	r3,0(r4)		/* phys address */
+#endif
 	PPC_LCMPI	0,r3,0
-	beqlr-				/* if no previous owner, done */
+	beqlr				/* if no previous owner, done */
+#ifdef CONFIG_PPC32
+	/* turn phys address into phys or virt based on offset */
+	lis	r6,PAGE_OFFSET@h
+	sub	r6, r6, r7
+	add	r3, r3, r6
+#endif
+2:
+	/* Save state to the thread struct */
+	mr	r6,r3
 	addi	r3,r3,THREAD	        /* want THREAD of task */
 	PPC_LL	r5,PT_REGS(r3)
 	PPC_LCMPI	0,r5,0
@@ -143,6 +245,9 @@
 	mffs	fr0
 	stfd	fr0,THREAD_FPSCR(r3)
 	beq	1f
+#ifdef CONFIG_PPC32
+	sub	r5, r5, r7
+#endif
 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 	li	r3,MSR_FP|MSR_FE0|MSR_FE1
 #ifdef CONFIG_VSX
@@ -153,11 +258,22 @@
 	andc	r4,r4,r3		/* disable FP for previous task */
 	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 1:
-#ifndef CONFIG_SMP
+	/*
+	 * If this is an ipi, make sure state is is commited before we
+	 * clear the lazy state pointers and return.  If a CPU is waiting on
+	 * this data (IPI case) then it won't start until FP_STATE is cleared
+	 */
+	LWSYNC /* make sure registers are in mem before say they are */
+	li	r5,TS_LAZY_STATE_INVALID
+	PPC_STL	r5,THREAD+THREAD_FP_STATE(r6)
 	li	r5,0
-	LOAD_REG_ADDRBASE(r4,last_task_used_math)
-	PPC_STL	r5,ADDROFF(last_task_used_math)(r4)
-#endif /* CONFIG_SMP */
+#ifdef CONFIG_PPC64
+	PPC_STL	r5,PACA_LAST_USED_FP(r13)
+#else
+	LAST_USED_FP_PTR(r3, r7, r4)
+	PPC_STL	r5,0(r3)
+#endif
+	LWSYNC
 	blr
 
 /*
Index: linux-lazy/arch/powerpc/kernel/paca.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/paca.c
+++ linux-lazy/arch/powerpc/kernel/paca.c
@@ -162,6 +162,7 @@
 	new_paca->hw_cpu_id = 0xffff;
 	new_paca->kexec_state = KEXEC_STATE_NONE;
 	new_paca->__current = &init_task;
+	new_paca->last_used_fp = NULL;
 #ifdef CONFIG_ALTIVEC
 	new_paca->last_used_altivec = NULL;
 #endif
Index: linux-lazy/arch/powerpc/kernel/process.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/process.c
+++ linux-lazy/arch/powerpc/kernel/process.c
@@ -58,7 +58,6 @@
 extern unsigned long _get_SP(void);
 
 #ifndef CONFIG_SMP
-struct task_struct *last_task_used_math = NULL;
 struct task_struct *last_task_used_vsx = NULL;
 struct task_struct *last_task_used_spe = NULL;
 #endif
@@ -69,45 +68,14 @@
  */
 void flush_fp_to_thread(struct task_struct *tsk)
 {
-	if (tsk->thread.regs) {
-		/*
-		 * We need to disable preemption here because if we didn't,
-		 * another process could get scheduled after the regs->msr
-		 * test but before we have finished saving the FP registers
-		 * to the thread_struct.  That process could take over the
-		 * FPU, and then when we get scheduled again we would store
-		 * bogus values for the remaining FP registers.
-		 */
-		preempt_disable();
-		if (tsk->thread.regs->msr & MSR_FP) {
-#ifdef CONFIG_SMP
-			/*
-			 * This should only ever be called for current or
-			 * for a stopped child process.  Since we save away
-			 * the FP register state on context switch on SMP,
-			 * there is something wrong if a stopped child appears
-			 * to still have its FP state in the CPU registers.
-			 */
-			BUG_ON(tsk != current);
-#endif
-			giveup_fpu(tsk);
-		}
-		preempt_enable();
-	}
+	giveup_fpu(0);
 }
 
 void enable_kernel_fp(void)
 {
 	WARN_ON(preemptible());
 
-#ifdef CONFIG_SMP
-	if (current->thread.regs && (current->thread.regs->msr & MSR_FP))
-		giveup_fpu(current);
-	else
-		giveup_fpu(NULL);	/* just enables FP for kernel */
-#else
-	giveup_fpu(last_task_used_math);
-#endif /* CONFIG_SMP */
+	giveup_fpu(0);
 }
 EXPORT_SYMBOL(enable_kernel_fp);
 
@@ -151,7 +119,7 @@
 
 void giveup_vsx(struct task_struct *tsk)
 {
-	giveup_fpu(tsk);
+	giveup_fpu(0);
 	giveup_altivec(0);
 	__giveup_vsx(tsk);
 }
@@ -210,12 +178,11 @@
 void discard_lazy_cpu_state(void)
 {
 	preempt_disable();
+	giveup_fpu(0);
 #ifdef CONFIG_ALTIVEC
 	giveup_altivec(0);
 #endif /* CONFIG_ALTIVEC */
 #ifndef CONFIG_SMP
-	if (last_task_used_math == current)
-		last_task_used_math = NULL;
 #ifdef CONFIG_VSX
 	if (last_task_used_vsx == current)
 		last_task_used_vsx = NULL;
@@ -378,6 +345,60 @@
 
 extern int csd_locked(struct call_single_data *data);
 
+/* Return value indicates if it was lazy or not */
+static bool switch_to_fp_lazy(struct task_struct *prev,
+			      struct task_struct *new)
+{
+	/*
+	 * At this point the FP reg state can be in 1 of 3 places
+	 * 1) cached on _this_ CPU.   Lazy/fast  :-)
+	 * 2) in the thread_struct.   Normal     :-|
+	 * 3) cached on another CPU.  Slow IPI   :-(
+         * .... lets go workout what happened....
+	 */
+
+	/* Cache the state pointer here incase it changes */
+	TS_LAZY_STATE_TYPE state = new->thread.fp_state;
+
+	/* Is the state here? */
+	if (state == LAZY_STATE_HERE) {
+		/* It's here! Excellent, simply turn FP on */
+		new->thread.regs->msr |= MSR_FP;
+		return true;
+	}
+	/*
+	 * If we have used FP in the past, but don't have lazy state,
+	 * then make sure we turn off FP.  load_up_fpu will deal
+	 * with saving the lazy state if we run an fp instruction
+	 */
+	new->thread.regs->msr &= ~MSR_FP;
+
+	if (state != TS_LAZY_STATE_INVALID) {
+#ifdef CONFIG_SMP
+		/*
+		 * To avoid a deadlock, make sure we don't
+		 * have someone else state here
+		 */
+		discard_lazy_cpu_state();
+
+		/*
+		 * Get the other CPU to flush it's state
+		 * synchronously.  It's possible this may may get run
+		 * multiple times, but giveup_fpu can handle this.
+		 */
+		if (!csd_locked(&(new->thread.fp_csd)))
+			__smp_call_function_single(
+				LAZY_STATE_CPU_ID,
+				&(new->thread.fp_csd),
+				0);
+#else /* CONFIG_SMP */
+		/* UP can't have state on another CPU */
+		BUG();
+#endif
+	}
+	return false;
+}
+
 #ifdef CONFIG_ALTIVEC
 /* Return value indicates if it was lazy or not */
 static bool switch_to_altivec_lazy(struct task_struct *prev,
@@ -451,21 +472,11 @@
 
 	/* Does next have lazy state somewhere? */
 	if (new->thread.regs) {
+		lazy &= switch_to_fp_lazy(prev, new);
 		lazy &= switch_to_altivec_lazy(prev, new);
 	}
 
 #ifdef CONFIG_SMP
-	/* avoid complexity of lazy save/restore of fpu
-	 * by just saving it every time we switch out if
-	 * this task used the fpu during the last quantum.
-	 *
-	 * If it tries to use the fpu again, it'll trap and
-	 * reload its fp regs.  So we don't have to do a restore
-	 * every switch, just a save.
-	 *  -- Cort
-	 */
-	if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP))
-		giveup_fpu(prev);
 #ifdef CONFIG_VSX
 	if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX))
 		/* VMX and FPU registers are already save here */
@@ -892,8 +903,15 @@
 #ifdef CONFIG_VSX
 	current->thread.used_vsr = 0;
 #endif
+#ifdef CONFIG_PPC_FPU
 	memset(current->thread.fpr, 0, sizeof(current->thread.fpr));
 	current->thread.fpscr.val = 0;
+	current->thread.used_fp = 0;
+	current->thread.fp_state = TS_LAZY_STATE_INVALID;
+	current->thread.fp_csd.func = giveup_fpu_ipi;
+	current->thread.fp_csd.info = 0;
+	current->thread.fp_csd.flags = 0;
+#endif /* CONFIG_PPC_FPU */
 #ifdef CONFIG_ALTIVEC
 	memset(current->thread.vr, 0, sizeof(current->thread.vr));
 	memset(&current->thread.vscr, 0, sizeof(current->thread.vscr));

^ permalink raw reply

* [RFC/PATCH 7/7] powerpc: Enable lazy save VSX for SMP
From: Michael Neuling @ 2010-12-06 23:40 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <20101206234043.083045003@neuling.org>

This enables lazy save of VSX state for SMP configurations.

Most of the logic for this is in the FP and VMX code, since VSX has no
additional state over these.  

When context switching to a new process:
 - if both VMX and FP state are on the CPU we are switch to, turn VSX
   on also.
 - if either FP or VMX are not on the CPU we are switch on, do not
   turn VSX on in the MSR.
We always start the new process at this point, irrespective of if we
have the FP and/or VMX state in the thread struct or current CPU.

When we take the vsx_unavailable exception, we first run load_up_fpu
and load_up_altivec to enable our state.  If either of these fail to
enable their respective MSR bits, the state has not arrived from the
IPI and hence we should bail to userspace with VSX off.  This will
enable IRQs, while we are waiting for the FP and VMX state. 

Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/system.h |    4 +-
 arch/powerpc/kernel/process.c     |   72 ++++++++++++++++++++------------------
 arch/powerpc/kernel/signal_32.c   |    2 -
 arch/powerpc/kernel/signal_64.c   |    2 -
 arch/powerpc/kernel/vector.S      |   48 +++----------------------
 5 files changed, 49 insertions(+), 79 deletions(-)

Index: linux-lazy/arch/powerpc/include/asm/system.h
===================================================================
--- linux-lazy.orig/arch/powerpc/include/asm/system.h
+++ linux-lazy/arch/powerpc/include/asm/system.h
@@ -150,8 +150,8 @@
 extern void giveup_altivec_ipi(void *);
 extern void load_up_altivec(struct task_struct *);
 extern int emulate_altivec(struct pt_regs *);
-extern void __giveup_vsx(struct task_struct *);
-extern void giveup_vsx(struct task_struct *);
+extern void __giveup_vsx(void);
+extern void giveup_vsx(void);
 extern void enable_kernel_spe(void);
 extern void giveup_spe(struct task_struct *);
 extern void load_up_spe(struct task_struct *);
Index: linux-lazy/arch/powerpc/kernel/process.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/process.c
+++ linux-lazy/arch/powerpc/kernel/process.c
@@ -58,7 +58,6 @@
 extern unsigned long _get_SP(void);
 
 #ifndef CONFIG_SMP
-struct task_struct *last_task_used_vsx = NULL;
 struct task_struct *last_task_used_spe = NULL;
 #endif
 
@@ -105,37 +104,21 @@
 {
 	WARN_ON(preemptible());
 
-#ifdef CONFIG_SMP
-	if (current->thread.regs && (current->thread.regs->msr & MSR_VSX))
-		giveup_vsx(current);
-	else
-		giveup_vsx(NULL);	/* just enable vsx for kernel - force */
-#else
-	giveup_vsx(last_task_used_vsx);
-#endif /* CONFIG_SMP */
+	giveup_vsx();
 }
 EXPORT_SYMBOL(enable_kernel_vsx);
 #endif
 
-void giveup_vsx(struct task_struct *tsk)
+void giveup_vsx(void)
 {
 	giveup_fpu(0);
 	giveup_altivec(0);
-	__giveup_vsx(tsk);
+	__giveup_vsx();
 }
 
 void flush_vsx_to_thread(struct task_struct *tsk)
 {
-	if (tsk->thread.regs) {
-		preempt_disable();
-		if (tsk->thread.regs->msr & MSR_VSX) {
-#ifdef CONFIG_SMP
-			BUG_ON(tsk != current);
-#endif
-			giveup_vsx(tsk);
-		}
-		preempt_enable();
-	}
+	giveup_vsx();
 }
 #endif /* CONFIG_VSX */
 
@@ -182,11 +165,11 @@
 #ifdef CONFIG_ALTIVEC
 	giveup_altivec(0);
 #endif /* CONFIG_ALTIVEC */
-#ifndef CONFIG_SMP
 #ifdef CONFIG_VSX
-	if (last_task_used_vsx == current)
-		last_task_used_vsx = NULL;
+	/* use __ version since fpu and altivec have been called already */
+	__giveup_vsx();
 #endif /* CONFIG_VSX */
+#ifndef CONFIG_SMP
 #ifdef CONFIG_SPE
 	if (last_task_used_spe == current)
 		last_task_used_spe = NULL;
@@ -462,26 +445,51 @@
 }
 #endif /* CONFIG_ALTIVEC */
 
+#ifdef CONFIG_VSX
+/* Return value indicates if it was lazy or not */
+static bool switch_to_vsx_lazy(struct task_struct *prev,
+			      struct task_struct *new,
+			      bool lazy)
+{
+	/* Is the state here? */
+	if (lazy) {
+		/* It's here! Excellent, simply turn VSX on */
+		new->thread.regs->msr |= MSR_VSX;
+		return true;
+	}
+	/*
+	 * If we have used VSX in the past, but don't have lazy state,
+	 * then make sure we turn off VSX.  load_up_vsx will deal
+	 * with saving the lazy state if we run a VSX instruction
+	 */
+	new->thread.regs->msr &= ~MSR_VSX;
+	return false;
+}
+#else /* CONFIG_VSX */
+static bool switch_to_vsx_lazy(struct task_struct *prev,
+			      struct task_struct *new,
+			      int lazy)
+{
+	return 1;
+}
+#endif /* CONFIG_VSX */
+
 struct task_struct *__switch_to(struct task_struct *prev,
 	struct task_struct *new)
 {
 	struct thread_struct *new_thread, *old_thread;
 	unsigned long flags;
 	struct task_struct *last;
-	int lazy = 1;
+	bool lazy = true;
 
 	/* Does next have lazy state somewhere? */
 	if (new->thread.regs) {
 		lazy &= switch_to_fp_lazy(prev, new);
 		lazy &= switch_to_altivec_lazy(prev, new);
+		switch_to_vsx_lazy(prev, new, lazy);
 	}
 
 #ifdef CONFIG_SMP
-#ifdef CONFIG_VSX
-	if (prev->thread.regs && (prev->thread.regs->msr & MSR_VSX))
-		/* VMX and FPU registers are already save here */
-		__giveup_vsx(prev);
-#endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
 	/*
 	 * If the previous thread used spe in the last quantum
@@ -495,10 +503,6 @@
 #endif /* CONFIG_SPE */
 
 #else  /* CONFIG_SMP */
-#ifdef CONFIG_VSX
-	if (new->thread.regs && last_task_used_vsx == new)
-		new->thread.regs->msr |= MSR_VSX;
-#endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
 	/* Avoid the trap.  On smp this this never happens since
 	 * we don't set last_task_used_spe
Index: linux-lazy/arch/powerpc/kernel/signal_32.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/signal_32.c
+++ linux-lazy/arch/powerpc/kernel/signal_32.c
@@ -452,7 +452,7 @@
 	 * contains valid data
 	 */
 	if (current->thread.used_vsr && ctx_has_vsx_region) {
-		__giveup_vsx(current);
+		__giveup_vsx();
 		if (copy_vsx_to_user(&frame->mc_vsregs, current))
 			return 1;
 		msr |= MSR_VSX;
Index: linux-lazy/arch/powerpc/kernel/signal_64.c
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/signal_64.c
+++ linux-lazy/arch/powerpc/kernel/signal_64.c
@@ -123,7 +123,7 @@
 	 * VMX data.
 	 */
 	if (current->thread.used_vsr && ctx_has_vsx_region) {
-		__giveup_vsx(current);
+		__giveup_vsx();
 		v_regs += ELF_NVRREG;
 		err |= copy_vsx_to_user(v_regs, current);
 		/* set MSR_VSX in the MSR value in the frame to
Index: linux-lazy/arch/powerpc/kernel/vector.S
===================================================================
--- linux-lazy.orig/arch/powerpc/kernel/vector.S
+++ linux-lazy/arch/powerpc/kernel/vector.S
@@ -257,50 +257,23 @@
 	beql+	load_up_fpu		/* skip if already loaded */
 	andis.	r5,r12,MSR_VEC@h
 	beql+	load_up_altivec		/* skip if already loaded */
-
-#ifndef CONFIG_SMP
-	ld	r3,last_task_used_vsx@got(r2)
-	ld	r4,0(r3)
-	cmpdi	0,r4,0
-	beq	1f
-	/* Disable VSX for last_task_used_vsx */
-	addi	r4,r4,THREAD
-	ld	r5,PT_REGS(r4)
-	ld	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
-	lis	r6,MSR_VSX@h
-	andc	r6,r4,r6
-	std	r6,_MSR-STACK_FRAME_OVERHEAD(r5)
-1:
-#endif /* CONFIG_SMP */
-	ld	r4,PACACURRENT(r13)
-	addi	r4,r4,THREAD		/* Get THREAD */
+/* state is all local now */
+	GET_CURRENT_THREAD(r5)
 	li	r6,1
-	stw	r6,THREAD_USED_VSR(r4) /* ... also set thread used vsr */
+	stw	r6,THREAD_USED_VSR(r5)
 	/* enable use of VSX after return */
 	oris	r12,r12,MSR_VSX@h
 	std	r12,_MSR(r1)
-#ifndef CONFIG_SMP
-	/* Update last_task_used_vsx to 'current' */
-	ld	r4,PACACURRENT(r13)
-	std	r4,0(r3)
-#endif /* CONFIG_SMP */
 	b	fast_exception_return
 
 /*
- * __giveup_vsx(tsk)
- * Disable VSX for the task given as the argument.
+ * __giveup_vsx()
+ * Disable VSX for current task
  * Does NOT save vsx registers.
- * Enables the VSX for use in the kernel on return.
+ * Doesn't enable kernel VSX on return (we could if need later)
  */
 _GLOBAL(__giveup_vsx)
-	mfmsr	r5
-	oris	r5,r5,MSR_VSX@h
-	mtmsrd	r5			/* enable use of VSX now */
-	isync
-
-	cmpdi	0,r3,0
-	beqlr-				/* if no previous owner, done */
-	addi	r3,r3,THREAD		/* want THREAD of task */
+	GET_CURRENT_THREAD(r3)
 	ld	r5,PT_REGS(r3)
 	cmpdi	0,r5,0
 	beq	1f
@@ -309,16 +282,9 @@
 	andc	r4,r4,r3		/* disable VSX for previous task */
 	std	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 1:
-#ifndef CONFIG_SMP
-	li	r5,0
-	ld	r4,last_task_used_vsx@got(r2)
-	std	r5,0(r4)
-#endif /* CONFIG_SMP */
 	blr
-
 #endif /* CONFIG_VSX */
 
-
 /*
  * The routines below are in assembler so we can closely control the
  * usage of floating-point registers.  These routines must be called

^ permalink raw reply

* Re: [PATCH 1/4] of: Add support for linking device tree blobs into vmlinux
From: Dirk Brandewie @ 2010-12-07  3:19 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: linux-arch, mmarek, microblaze-uclinux, devicetree-discuss,
	linux-kernel, sodaville, linuxppc-dev
In-Reply-To: <20101206190251.GB20882@merkur.ravnborg.org>

On 12/06/2010 11:02 AM, Sam Ravnborg wrote:
> On Mon, Dec 06, 2010 at 09:35:59AM -0800, dirk.brandewie@gmail.com wrote:
>> From: Dirk Brandewie<dirk.brandewie@gmail.com>
>> -
>> +# DTC
>> +#  ---------------------------------------------------------------------------
>> +
>> +# Generate an assembly file to wrap the output of the device tree compiler
>> +$(obj)/%.dtb.S: $(obj)/%.dtb
>> +	@echo '#include<asm-generic/vmlinux.lds.h>'>  $@
>> +	@echo '.section .dtb.init.rodata,"a"'>>  $@
>> +	@echo '.balign STRUCT_ALIGNMENT'>>  $@
>> +	@echo '.global __dtb_$(*F)_begin'>>  $@
>> +	@echo '__dtb_$(*F)_begin:'>>  $@
>> +	@echo '.incbin "$<" '>>  $@
>> +	@echo '__dtb_$(*F)_end:'>>  $@
>> +	@echo '.global __dtb_$(*F)_end'>>  $@
>> +	@echo '.balign STRUCT_ALIGNMENT'>>  $@
>> +
>
> If we really want this rule in Makefile.lib then at least make it less verbose,
> and more secure.

I started with the change in Makefile.lib because it made sense to me, is there 
a more appropriate place for these rules/commands?

> Something like this:
> quiet_dt_S_dtb_cmd = DTB    $@
>        dt_S_dtb_cmd =                                      \
> (                                                         \
> 	@echo '#include<asm-generic/vmlinux.lds.h>';     \
> 	@echo '.section .dtb.init.rodata,"a"';            \
> 	@echo '.balign STRUCT_ALIGNMENT';                 \
> 	echo '.global __dtb_$(*F)_begin';                 \
> 	echo '__dtb_$(*F)_begin:';                        \
> 	echo '.incbin "$<" ';                             \
> 	echo '__dtb_$(*F)_end:';                          \
> 	echo '.global __dtb_$(*F)_end';                   \
> 	echo '.balign STRUCT_ALIGNMENT';                  \
> )>  $@
>
> $(obj)/%.dtb.S: $(obj)/%.dtb
> 	$(call cmd,dt_S_dtb)
>

I will make the changes in next version.

>
>> +DTC = $(objtree)/scripts/dtc/dtc
>
> If this is the only spot where we use DTC then drop the variable.
>
>> +
>> +quiet_cmd_dtc = DTC $@
>> +      cmd_dtc = $(DTC) -O dtb -o $@ -b 0 $(DTC_FLAGS) $<
>
>
>> +ooo
>
> What is the purpose of these "ooo"? A debugging left-over?
>

Yep sorry

>
> 	Sam

^ permalink raw reply

* MPC831x (and others?) NAND erase performance improvements
From: Mark Mason @ 2010-12-07  3:15 UTC (permalink / raw)
  To: linuxppc-dev

A few months ago I ran into some performance problems involving
UBI/NAND erases holding other devices off the LBC on an MPC8315.  I
found a solution for this, which worked well, at least with the
hardware I was working with.  I suspect the same problem affects other
PPCs, probably including multicore devices, and maybe other
architectures as well.

I don't have experience with similar NAND controllers on other
devices, so I'd like to explain what I found and see if someone who's
more familiar with the family and/or driver can tell if this is
useful.

The problem cropped up when there was a lot of traffic to the NAND
(Samsung K9WAGU08U1B-PIB0), with the NAND being on the LBC along with
a video chip that needed constant and prompt attention.

What I would see is that, as the writes happened, the erases would
wind up batched and issued all at once, such that frequently 400-700
erases were issued in rapid succession with a 1ms LBC BUSY cycle per
erase.  BUSY was shared with all of the devices on the LBC, so the PPC
could not talk to the video chip as long as BUSY was asserted by the
NAND.  This would give us a window of up to 700ms in which the PPC
could manage very little communication with other devices on the LBC -
in our case the video chip, for which this delay was essentially
fatal.  I suspect that some multicore chips might have one core
effectively halt if that core attempts to access the LBC while the
other core (or itself, for that matter) is executing an erase (if they
have a similar NAND controller).

What I found, though, was that the NAND did not inherently assert BUSY
as part of the erase - BUSY was asserted because the driver polled for
the status (NAND_CMD_STATUS).  If the status poll was delayed for the
duration of the erase then the MPC could talk to the video chip while
the erase was in progress.  At the end of the 1ms delay I would then
poll for status, which would complete effectively immediately.

Here's a code snippet from 2.6.37, with some comments I added.
drivers/mtd/nand/fsl_elbc_nand.c - fsl_elbc_cmdfunc():

  /* ERASE2 uses the block and page address from ERASE1 */
  case NAND_CMD_ERASE2:
    dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");

    out_be32(&lbc->fir,
       (FIR_OP_CM0 << FIR_OP0_SHIFT) |  /* Execute CMD0 (ERASE1).           */
       (FIR_OP_PA  << FIR_OP1_SHIFT) |  /* Issue block and page address.    */
       (FIR_OP_CM2 << FIR_OP2_SHIFT) |  /* Execute CMD2 (ERASE2).           */
           /* (delay needed here - this is where the erase happens) */
       (FIR_OP_CW1 << FIR_OP3_SHIFT) |  /* Wait for LFRB (BUSY) to deassert */
                                        /* then issue CW1 (read status).    */
       (FIR_OP_RS  << FIR_OP4_SHIFT));  /* Read one byte.                   */

    out_be32(&lbc->fcr,
       (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |  /* 0x60 */
       (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |  /* 0x70 */
       (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));  /* 0xD0 */

    out_be32(&lbc->fbcr, 0);
    elbc_fcm_ctrl->read_bytes = 0;
    elbc_fcm_ctrl->use_mdr = 1;

    fsl_elbc_run_command(mtd);
    return;

What I did was to issue two commands with fsl_elbc_run_command(), with
a 1ms sleep in between (a tightloop delay worked almost as well, the
important part was having 1ms between the erase and the status poll).
The first command did the FIR_OP_CM0 (NAND_CMD_ERASE1), FIR_OP_PA, and
FIR_OP_CM2 (NAND_CMD_ERASE2).  The second did the FIR_OP_CW1
(NAND_CMD_STATUS) and FIR_OP_RS.

For a bit more detail...  fsl_elbc_run_command() would put the thread
issuing the erase to sleep so other threads could run.  That did work
as planned, except that I was working with a fairly pathalogical case
- there was a very high volume of writes to the NAND, and the video
chip required very frequent and prompt attention.  This meant that the
thread that was most likely to run when the NAND erase was in progress
was the thread that serviced the video chip.

A logic analyzer backed this up.  It would show the erase being
issued, BUSY (R/B# or LFRB) being asserted for 1ms, one or two 16 bit
transactions to the video chip, then another erase, repeating this
process hundreds of times in a row.  The UBI BGT would run long enough
to issue an erase (probably on the order of 20us) then go to sleep.
The video thread would then run, and issue a transaction to the chip.
That transaction would get blocked until BUSY deasserted, at which
point the thread would appear to have run for 1ms, even though it had
only executed a single bus transaction.

I know almost nothing at all about the scheduler, but I'm pretty sure
that this behavior would cause the scheduler to think the video thread
was a CPU hog, since the video thread was running for 1ms for every
20us that the UBI BGT ran, which would cause the scheduler to unfairly
prefer the UBI BGT.  I initially tried to address this problem with
thread priorities, but the unfortunate reality was that either the
NAND writes could fall behind or the video chip could fall behind, and
there wasn't spare bandwidth to allow either.

I tried the same trick for the writes.  It didn't work.

I really hope that someone cares enough after all this typing.
Unfortunately I don't have access to the hardware in question any
more, so I'm a bit limited in what I can offer beyond this without
hardware to run on, but I am willing to do whatever I can.

^ permalink raw reply

* [PATCH 5/6] trace, powerpc: Implement raw syscall tracepoints on PowerPC
From: Ian Munsie @ 2010-12-07  4:29 UTC (permalink / raw)
  To: Avantika Mathur
  Cc: Andreas Dilger, Dave Kleikamp, Jiri Kosina, Jason Baron,
	linuxppc-dev, Steven Rostedt, Alexander Graf, Ingo Molnar,
	Paul Mackerras, Ian Munsie, KOSAKI Motohiro, Frederic Weisbecker,
	Scott Wood, Nathan Lynch, Andrew Morton, David Gibson,
	Andreas Schwab, Namhyung Kim, linux-kernel
In-Reply-To: <1291696151-4336-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au.ibm.com>

This patch implements the raw syscall tracepoints on PowerPC and exports
them for ftrace syscalls to use.

To minimise reworking existing code, I slightly re-ordered the thread
info flags such that the new TIF_SYSCALL_TRACEPOINT bit would still fit
within the 16 bits of the andi. instruction's UI field. The instructions
in question are in /arch/powerpc/kernel/entry_{32,64}.S to and the
_TIF_SYSCALL_T_OR_A with the thread flags to see if system call tracing
is enabled.

In the case of 64bit PowerPC, arch_syscall_addr and
arch_syscall_match_sym_name are overridden to allow ftrace syscalls to
work given the unusual system call table structure and symbol names that
start with a period.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/Kconfig                   |    1 +
 arch/powerpc/include/asm/syscall.h     |    5 +++++
 arch/powerpc/include/asm/thread_info.h |    7 +++++--
 arch/powerpc/kernel/Makefile           |    1 +
 arch/powerpc/kernel/ftrace.c           |   19 +++++++++++++++++++
 arch/powerpc/kernel/ptrace.c           |   10 ++++++++++
 6 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index e625e9e..6614b69 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -140,6 +140,7 @@ config PPC
 	select HAVE_PERF_EVENTS
 	select HAVE_REGS_AND_STACK_ACCESS_API
 	select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64
+	select HAVE_SYSCALL_TRACEPOINTS
 
 config EARLY_PRINTK
 	bool
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 23913e9..b54b2ad 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -15,6 +15,11 @@
 
 #include <linux/sched.h>
 
+/* ftrace syscalls requires exporting the sys_call_table */
+#ifdef CONFIG_FTRACE_SYSCALLS
+extern const unsigned long *sys_call_table;
+#endif /* CONFIG_FTRACE_SYSCALLS */
+
 static inline long syscall_get_nr(struct task_struct *task,
 				  struct pt_regs *regs)
 {
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 65eb859..4403f09 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -110,7 +110,8 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_NOERROR		12	/* Force successful syscall return */
 #define TIF_NOTIFY_RESUME	13	/* callback before returning to user */
 #define TIF_FREEZE		14	/* Freezing for suspend */
-#define TIF_RUNLATCH		15	/* Is the runlatch enabled? */
+#define TIF_SYSCALL_TRACEPOINT	15	/* syscall tracepoint instrumentation */
+#define TIF_RUNLATCH		16	/* Is the runlatch enabled? */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -127,8 +128,10 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_NOERROR		(1<<TIF_NOERROR)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
+#define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
 #define _TIF_RUNLATCH		(1<<TIF_RUNLATCH)
-#define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
+#define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+				 _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
 
 #define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
 				 _TIF_NOTIFY_RESUME)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 36c30f3..13d6eb0 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -106,6 +106,7 @@ obj64-$(CONFIG_AUDIT)		+= compat_audit.o
 
 obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS)	+= ftrace.o
 obj-$(CONFIG_PERF_EVENTS)	+= perf_callchain.o
 
 obj-$(CONFIG_PPC_PERF_CTRS)	+= perf_event.o
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index ce1f3e4..f5fadbb 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -22,6 +22,7 @@
 #include <asm/cacheflush.h>
 #include <asm/code-patching.h>
 #include <asm/ftrace.h>
+#include <asm/syscall.h>
 
 
 #ifdef CONFIG_DYNAMIC_FTRACE
@@ -600,3 +601,21 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
 	}
 }
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64)
+unsigned long __init arch_syscall_addr(int nr)
+{
+	return sys_call_table[nr*2];
+}
+
+inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
+{
+	/*
+	 * Compare the symbol name with the system call name. Skip the .sys or
+	 * .SyS prefix from the symbol name and the sys prefix from the system
+	 * call name and just match the rest. 32bit can use the generic
+	 * function since their symbol names don't start with a period.
+	 */
+	return (!strcmp(sym + 4, name + 3));
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index a9b3296..f70d144 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -29,6 +29,7 @@
 #include <linux/signal.h>
 #include <linux/seccomp.h>
 #include <linux/audit.h>
+#include <trace/syscall.h>
 #ifdef CONFIG_PPC32
 #include <linux/module.h>
 #endif
@@ -40,6 +41,9 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
 /*
  * The parameter save area on the stack is used to store arguments being passed
  * to callee function and is located at fixed offset from stack pointer.
@@ -1681,6 +1685,9 @@ long do_syscall_trace_enter(struct pt_regs *regs)
 		 */
 		ret = -1L;
 
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_enter(regs, regs->gpr[0]);
+
 	if (unlikely(current->audit_context)) {
 #ifdef CONFIG_PPC64
 		if (!is_32bit_task())
@@ -1709,6 +1716,9 @@ void do_syscall_trace_leave(struct pt_regs *regs)
 		audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
 				   regs->result);
 
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_exit(regs, regs->result);
+
 	step = test_thread_flag(TIF_SINGLESTEP);
 	if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 		tracehook_report_syscall_exit(regs, step);
-- 
1.7.2.3

^ permalink raw reply related

* PowerPC, ftrace: Add PPC raw syscall tracepoints & ftrace fixes (subset from v2)
From: Ian Munsie @ 2010-12-07  5:00 UTC (permalink / raw)
  To: linux-kernel
  Cc: Andreas Dilger, Dave Kleikamp, Andrew Morton, Jiri Kosina,
	Jason Baron, linuxppc-dev, Steven Rostedt, Alexander Graf,
	Ingo Molnar, Paul Mackerras, KOSAKI Motohiro, Frederic Weisbecker,
	Scott Wood, Nathan Lynch, Avantika Mathur, David Gibson,
	Andreas Schwab, Namhyung Kim
In-Reply-To: <1291696151-4336-1-git-send-email-imunsie@au1.ibm.com>

Oops, It seems that I missed LKML in the to address of the cover email
for this set. Guess I shouldn't send a patch set just as the fire alarm
goes off.  Resending:


Hi all,

This is a partial version of my 'ftrace syscalls, PowerPC: Various fixes,
Compat Syscall support and PowerPC implementation'.

I've been tied up with other work and haven't had time to complete the
work on the v3 of the full set, but the patches I'm including here are
pretty stable and there has been a request for the raw syscall
tracepoints in PowerPC.

The actual patches included here have not been changed from the v2 other than
rebasing them on tip/master. They add support for *raw* syscall tracepoints on
PowerPC, while fixing ftrace syscalls to ensure that events will only be created
for syscalls that successfully map their metadata to a syscall number,
so that non-working phantom events are not created.

Patch #2 and #6 in this series are not strictly necessary for this, they just
optimise ftrace syscalls a bit.


What's missing from this series that was in the v2 is the conversion of
all the syscalls implemented under /arch/powerpc, Jason Baron's compat
syscall support and the conversion of the remaining native and compat
syscalls to this infrastructure.

Cheers,
-Ian

GIT: [PATCH 1/6] ftrace syscalls: don't add events for unmapped syscalls
GIT: [PATCH 2/6] trace syscalls: Remove redundant syscall_nr checks
GIT: [PATCH 3/6] ftrace syscalls: Make arch_syscall_addr weak
GIT: [PATCH 4/6] ftrace syscalls: Allow arch specific syscall symbol matching
GIT: [PATCH 5/6] trace, powerpc: Implement raw syscall tracepoints on PowerPC
GIT: [PATCH 6/6] trace syscalls: Early terminate search for sys_ni_syscall

^ permalink raw reply

* Run 'usermode-agent' cause kernel panic on Powerpc
From: xufeng zhang @ 2010-12-07  6:48 UTC (permalink / raw)
  To: shaggy, Linuxppc-dev

Hi Dave,

I have a question with the below patch you made before:
----------------------------------------
powerpc/booke: Add support for advanced debug registers

From: Dave Kleikamp <shaggy@linux.vnet.ibm.com>

Based on patches originally written by Torez Smith.
-----------------------------------------

I meet a kernel panic problem while running 'usermode-agent' on PowerPC
----------------------------------------
Oops: Exception in kernel mode, sig: 5 [#1]
PREEMPT LTT NESTING LEVEL : 0
MPC8536 DS
last sysfs file: 
/sys/devices/f3000000.soc/f3003100.i2c/i2c-1/i2c-dev/i2c-1/dev
Modules linked in:
NIP: c00081a0 LR: c03a9560 CTR: c003547c
REGS: ef11bf10 TRAP: 2002   Not tainted  (2.6.34.6-WR4.0.0.0_standard)
MSR: 00021000 <ME,CE>  CR: 44000624  XER: 00000000
TASK = efc1de00[752] 'usermode-agent' THREAD: ef63e000
GPR00: cc00cc00 ef63fe60 efc1de00 efc1de00 efc1f700 c04c8000 00258560 
ffffffff
GPR08: ffda8a00 40000000 00001fda c0500000 49eaebbd 1008b654 3ff8a900 
00000000
GPR16: 00000000 eed84c40 c03b4570 c04ee4d0 c04ca870 ef63e03c 00000000 
00000000
GPR24: c04f7ee8 c04ee4c0 00000004 c04ca440 ef63e000 efc1f700 c04ca440 
efc1de00
NIP [c00081a0] __switch_to+0xac/0x104
LR [c03a9560] schedule+0x20c/0x3f4
Call Trace:
[ef63fe60] [efc1f700] 0xefc1f700 (unreliable)
[ef63fe70] [c03a9560] schedule+0x20c/0x3f4
[ef63fec0] [c00429e0] do_wait+0x1a4/0x278
[ef63fef0] [c0042b44] sys_wait4+0x90/0xf8
[ef63ff40] [c00106d4] ret_from_syscall+0x0/0x4
------------------------------------------

Actually, this problem is caused by enabling On Chip Debugging, when On
Chip Debugging is enabled, we enable MSR_DE as below:
#define MSR_KERNEL      (MSR_ME|MSR_RI|MSR_CE|MSR_DE)

If I comment out "mtspr(SPRN_DBCR0, thread->dbcr0);" in 
prime_debug_regs() function,
then it will be ok.

Here is my analysis for this problem:
Run 'usermode-agent' application will set Internal Debug Mode(IDM) and
Instruction Complete Debug Event(ICMP)flags for thread.
As MSR_DE is enabled, when execute context switching in prime_debug_regs(),
thread->dbcr0 would write to SPRN_DBCR0 register.
So this will enable Instruction Complete Debug Event interrupt, and it 
will cause a kernel-mode
exception right now, it will be handled in native_DebugException(), then 
kernel detected
this exception not happens in user-mode, lastly kernel call die() and 
kill current process.

So my question is could I just comment out "mtspr(SPRN_DBCR0, 
thread->dbcr0);" in prime_debug_regs()?
I'm sure whether or not it will impose a bad impact on debugging.
Thanks in advance!


Thanks,
Xufeng Zhang

^ permalink raw reply

* [PATCH v2] powerpc, 5200: add support for charon board
From: Heiko Schocher @ 2010-12-07  6:58 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Heiko Schocher
In-Reply-To: <1291450906-22445-1-git-send-email-hs@denx.de>

Signed-off-by: Heiko Schocher <hs@denx.de>
cc: Wolfram Sang <w.sang@pengutronix.de>
---
- based against 2.6.37-rc4

./scripts/checkpatch.pl 0001-powerpc-5200-add-support-for-charon-board.patch
total: 0 errors, 0 warnings, 233 lines checked

0001-powerpc-5200-add-support-for-charon-board.patch has no obvious style problems and is ready for submission.

- changes since v1:
  add comments from Wolfram Sang
  - no defconfig file
  - comment corrected in DTS
  - boardlist sorted alphabetically
  - commit log without boardinfo

 arch/powerpc/boot/dts/charon.dts             |  226 ++++++++++++++++++++++++++
 arch/powerpc/platforms/52xx/mpc5200_simple.c |    1 +
 2 files changed, 227 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/boot/dts/charon.dts

diff --git a/arch/powerpc/boot/dts/charon.dts b/arch/powerpc/boot/dts/charon.dts
new file mode 100644
index 0000000..9776889
--- /dev/null
+++ b/arch/powerpc/boot/dts/charon.dts
@@ -0,0 +1,226 @@
+/*
+ * charon board Device Tree Source
+ *
+ * Copyright (C) 2007 Semihalf
+ * Marian Balakowicz <m8@semihalf.com>
+ *
+ * Copyright (C) 2010 DENX Software Engineering GmbH
+ * Heiko Schocher <hs@denx.de>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+/dts-v1/;
+
+/ {
+	model = "anon,charon";
+	compatible = "anon,charon";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	interrupt-parent = <&mpc5200_pic>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		PowerPC,5200@0 {
+			device_type = "cpu";
+			reg = <0>;
+			d-cache-line-size = <32>;
+			i-cache-line-size = <32>;
+			d-cache-size = <0x4000>;	// L1, 16K
+			i-cache-size = <0x4000>;	// L1, 16K
+			timebase-frequency = <0>;	// from bootloader
+			bus-frequency = <0>;		// from bootloader
+			clock-frequency = <0>;		// from bootloader
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x08000000>;	// 128MB
+	};
+
+	soc5200@f0000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "fsl,mpc5200-immr";
+		ranges = <0 0xf0000000 0x0000c000>;
+		reg = <0xf0000000 0x00000100>;
+		bus-frequency = <0>;		// from bootloader
+		system-frequency = <0>;		// from bootloader
+
+		cdm@200 {
+			compatible = "fsl,mpc5200-cdm";
+			reg = <0x200 0x38>;
+		};
+
+		mpc5200_pic: interrupt-controller@500 {
+			// 5200 interrupts are encoded into two levels;
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			compatible = "fsl,mpc5200-pic";
+			reg = <0x500 0x80>;
+		};
+
+		timer@600 {	// General Purpose Timer
+			compatible = "fsl,mpc5200-gpt";
+			reg = <0x600 0x10>;
+			interrupts = <1 9 0>;
+			fsl,has-wdt;
+		};
+
+		can@900 {
+			compatible = "fsl,mpc5200-mscan";
+			interrupts = <2 17 0>;
+			reg = <0x900 0x80>;
+		};
+
+		can@980 {
+			compatible = "fsl,mpc5200-mscan";
+			interrupts = <2 18 0>;
+			reg = <0x980 0x80>;
+		};
+
+		gpio_simple: gpio@b00 {
+			compatible = "fsl,mpc5200-gpio";
+			reg = <0xb00 0x40>;
+			interrupts = <1 7 0>;
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		usb@1000 {
+			compatible = "fsl,mpc5200-ohci","ohci-be";
+			reg = <0x1000 0xff>;
+			interrupts = <2 6 0>;
+		};
+
+		dma-controller@1200 {
+			device_type = "dma-controller";
+			compatible = "fsl,mpc5200-bestcomm";
+			reg = <0x1200 0x80>;
+			interrupts = <3 0 0  3 1 0  3 2 0  3 3 0
+			              3 4 0  3 5 0  3 6 0  3 7 0
+			              3 8 0  3 9 0  3 10 0  3 11 0
+			              3 12 0  3 13 0  3 14 0  3 15 0>;
+		};
+
+		xlb@1f00 {
+			compatible = "fsl,mpc5200-xlb";
+			reg = <0x1f00 0x100>;
+		};
+
+		serial@2000 {		// PSC1
+			compatible = "fsl,mpc5200-psc-uart";
+			reg = <0x2000 0x100>;
+			interrupts = <2 1 0>;
+		};
+
+		serial@2400 {		// PSC3
+			compatible = "fsl,mpc5200-psc-uart";
+			reg = <0x2400 0x100>;
+			interrupts = <2 3 0>;
+		};
+
+		ethernet@3000 {
+			compatible = "fsl,mpc5200-fec";
+			reg = <0x3000 0x400>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			interrupts = <2 5 0>;
+			fixed-link = <1 1 100 0 0>;
+		};
+
+		mdio@3000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl,mpc5200-mdio";
+			reg = <0x3000 0x400>;       // fec range, since we need to setup fec interrupts
+			interrupts = <2 5 0>;   // these are for "mii command finished", not link changes & co.
+		};
+
+		ata@3a00 {
+			compatible = "fsl,mpc5200-ata";
+			reg = <0x3a00 0x100>;
+			interrupts = <2 7 0>;
+		};
+
+		i2c@3d00 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl,mpc5200-i2c","fsl-i2c";
+			reg = <0x3d00 0x40>;
+			interrupts = <2 15 0>;
+		};
+
+
+		i2c@3d40 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl,mpc5200-i2c","fsl-i2c";
+			reg = <0x3d40 0x40>;
+			interrupts = <2 16 0>;
+
+			dtt@28 {
+				compatible = "national,lm80";
+				reg = <0x28>;
+			};
+
+			rtc@68 {
+				compatible = "dallas,ds1374";
+				reg = <0x68>;
+			};
+		};
+
+		sram@8000 {
+			compatible = "fsl,mpc5200-sram";
+			reg = <0x8000 0x4000>;
+		};
+	};
+
+	localbus {
+		compatible = "fsl,mpc5200-lpb","simple-bus";
+		#address-cells = <2>;
+		#size-cells = <1>;
+		ranges = <	0 0 0xfc000000 0x02000000
+				3 0 0xe8000000 0x00080000>;
+
+		flash@0,0 {
+			compatible = "cfi-flash";
+			reg = <0 0 0x02000000>;
+			bank-width = <4>;
+			device-width = <2>;
+			#size-cells = <1>;
+			#address-cells = <1>;
+		};
+
+		mram0@3,0 {
+			compatible = "mtd-ram";
+			reg = <3 0x00000 0x80000>;
+			bank-width = <1>;
+		};
+	};
+
+	pci@f0000d00 {
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		device_type = "pci";
+		compatible = "fsl,mpc5200-pci";
+		reg = <0xf0000d00 0x100>;
+		interrupt-map-mask = <0xf800 0 0 7>;
+		interrupt-map = <0xc000 0 0 1 &mpc5200_pic 0 0 3
+				 0xc000 0 0 2 &mpc5200_pic 0 0 3
+				 0xc000 0 0 3 &mpc5200_pic 0 0 3
+				 0xc000 0 0 4 &mpc5200_pic 0 0 3>;
+		clock-frequency = <0>; // From boot loader
+		interrupts = <2 8 0 2 9 0 2 10 0>;
+		bus-range = <0 0>;
+		ranges = <0x42000000 0 0x80000000 0x80000000 0 0x10000000
+			  0x02000000 0 0x90000000 0x90000000 0 0x10000000
+			  0x01000000 0 0x00000000 0xa0000000 0 0x01000000>;
+	};
+};
diff --git a/arch/powerpc/platforms/52xx/mpc5200_simple.c b/arch/powerpc/platforms/52xx/mpc5200_simple.c
index d45be5b..37cb892 100644
--- a/arch/powerpc/platforms/52xx/mpc5200_simple.c
+++ b/arch/powerpc/platforms/52xx/mpc5200_simple.c
@@ -50,6 +50,7 @@ static void __init mpc5200_simple_setup_arch(void)
 
 /* list of the supported boards */
 static char *board[] __initdata = {
+	"anon,charon",
 	"intercontrol,digsy-mtc",
 	"manroland,mucmc52",
 	"manroland,uc101",
-- 
1.7.2.3

^ permalink raw reply related

* [PATCH 2/2 v2] powerpc, video: add SM501 support for charon board.
From: Heiko Schocher @ 2010-12-07  6:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-fbdev, Heiko Schocher, devicetree-discuss
In-Reply-To: <1291451028-22532-2-git-send-email-hs@denx.de>

Signed-off-by: Heiko Schocher <hs@denx.de>
cc: linux-fbdev@vger.kernel.org
cc: devicetree-discuss@ozlabs.org
---
- based against 2.6.37-rc4

./scripts/checkpatch.pl 0003-powerpc-video-add-SM501-support-for-charon-board.patch
total: 0 errors, 0 warnings, 21 lines checked

0003-powerpc-video-add-SM501-support-for-charon-board.patch has no obvious style problems and is ready for submission.

- changes since v1:
  - no board specific defconfig file for mpc52xx based boards as suggested
    from Wolfram Sang

 arch/powerpc/boot/dts/charon.dts |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/boot/dts/charon.dts b/arch/powerpc/boot/dts/charon.dts
index 9776889..d9af022 100644
--- a/arch/powerpc/boot/dts/charon.dts
+++ b/arch/powerpc/boot/dts/charon.dts
@@ -186,6 +186,7 @@
 		#address-cells = <2>;
 		#size-cells = <1>;
 		ranges = <	0 0 0xfc000000 0x02000000
+				1 0 0xe0000000 0x04000000 // CS1 range, SM501
 				3 0 0xe8000000 0x00080000>;
 
 		flash@0,0 {
@@ -197,6 +198,14 @@
 			#address-cells = <1>;
 		};
 
+		display@1,0 {
+			compatible = "smi,sm501";
+			reg = <1 0x00000000 0x00800000
+			       1 0x03e00000 0x00200000>;
+			mode = "640x480-32@60";
+			interrupts = <1 1 3>;
+		};
+
 		mram0@3,0 {
 			compatible = "mtd-ram";
 			reg = <3 0x00000 0x80000>;
-- 
1.7.2.3

^ permalink raw reply related

* Re: PowerMac G4 (Digital Audio)
From: Risto Suominen @ 2010-12-07  8:51 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev
In-Reply-To: <1291562178.3495.6.camel@jlt3.sipsolutions.net>

2010/12/5, Johannes Berg <johannes@sipsolutions.net>:
>
>> http://ristosu.wippiespace.com/pub/alsa-tumbler-1.0.22.1-p15.diff
>> http://ristosu.wippiespace.com/pub/alsa-tumbler-1.0.22.1-p16.diff
>
> These patches are odd -- the first one adds something the second
> removes? Am I supposed to look at the combination?
>
Not really, the second one adds gpio16 but still keeps gpio15.
>
> But frankly, it's been so long that it's not all making perfect sense to
> me right now. If you send me a tarball of /proc/device-tree/ maybe I can
> take a look -- just looked at my collection and I don't have that one.
>
> johannes
>
A device tree dump is located at
http://manulix.wikidot.com/hidden:device-tree-my_G4
The tarball can be wget-ted from
http://manulix.wikidot.com/local--files/hidden:device-tree-my-g4/device-tree.tar.bz2

Risto

^ permalink raw reply

* RE: MPC831x (and others?) NAND erase performance improvements
From: David Laight @ 2010-12-07  9:00 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20101207031554.GA12731@postdiluvian.org>

=20
> The problem cropped up when there was a lot of traffic to the NAND
> (Samsung K9WAGU08U1B-PIB0), with the NAND being on the LBC along with
> a video chip that needed constant and prompt attention.
>=20
> What I would see is that, as the writes happened, the erases would
> wind up batched and issued all at once, such that frequently 400-700
> erases were issued in rapid succession with a 1ms LBC BUSY cycle per
> erase.

Are those just the reads of the status register polling to
determine when the sector erase has completed ?
In which case a software delay beteen the reads might work.

Writes probably also have to be polled, but the individual
writes happen faster.

It is possible that an uncached read of another memory area
will stall the cpu long enough to allow another LBC master in.
One every few writes might be enough.
I had to do something similar on rather different hardware ...

	David

^ permalink raw reply

* Re: PowerMac G4 (Digital Audio)
From: Johannes Berg @ 2010-12-07 10:18 UTC (permalink / raw)
  To: Risto Suominen; +Cc: linuxppc-dev
In-Reply-To: <AANLkTin-wbJd6qSMnB0GL=mGL_u-cUY00pEHaWo-hpwK@mail.gmail.com>

On Tue, 2010-12-07 at 10:51 +0200, Risto Suominen wrote:
> 2010/12/5, Johannes Berg <johannes@sipsolutions.net>:
> >
> >> http://ristosu.wippiespace.com/pub/alsa-tumbler-1.0.22.1-p15.diff
> >> http://ristosu.wippiespace.com/pub/alsa-tumbler-1.0.22.1-p16.diff
> >
> > These patches are odd -- the first one adds something the second
> > removes? Am I supposed to look at the combination?
> >
> Not really, the second one adds gpio16 but still keeps gpio15.

Oh, so the change to "gpio1" was intended to be some wildcard?

> > But frankly, it's been so long that it's not all making perfect sense to
> > me right now. If you send me a tarball of /proc/device-tree/ maybe I can
> > take a look -- just looked at my collection and I don't have that one.

> A device tree dump

Hmm ok, it's not really making a lot of sense to me now either.

Can you hack the file I previously pointed out and see if that helps?

johannes

^ permalink raw reply

* Re: PowerMac G4 (Digital Audio)
From: Risto Suominen @ 2010-12-07 11:27 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev
In-Reply-To: <1291717126.3607.17.camel@jlt3.sipsolutions.net>

2010/12/7, Johannes Berg <johannes@sipsolutions.net>:
>
> Oh, so the change to "gpio1" was intended to be some wildcard?
>
Yes. I know, not very pretty.
>
> Can you hack the file I previously pointed out and see if that helps?
>
Well, I could try. Actually, I don't own a machine to test with.

So, as far as I can see, you don't have any special handling for this
machine, or any other either? And you don't look for these gpio15 (hp)
and gpio16 (lo). They are the detect inputs, not mute outputs. So can
they cause such problems that no sound is played? Hmm...

One solution could be to just remove the support from snd-aoa and
continue using snd-powermac. Now it's difficult because of the
auto-loading.

Risto

^ permalink raw reply

* Re: PowerMac G4 (Digital Audio)
From: Johannes Berg @ 2010-12-07 11:30 UTC (permalink / raw)
  To: Risto Suominen; +Cc: linuxppc-dev
In-Reply-To: <AANLkTik=V0_2_jtQgCnE7jUh_a-Fr+JvcTqUzy1YfLKs@mail.gmail.com>

On Tue, 2010-12-07 at 13:27 +0200, Risto Suominen wrote:

> > Can you hack the file I previously pointed out and see if that helps?
> >
> Well, I could try. Actually, I don't own a machine to test with.

Oh, ok... I thought you did.

> So, as far as I can see, you don't have any special handling for this
> machine, or any other either? And you don't look for these gpio15 (hp)
> and gpio16 (lo). They are the detect inputs, not mute outputs. So can
> they cause such problems that no sound is played? Hmm...

Right, normally we don't need any special handling since the DT has the
right names ... this machine just needs an override for the GPIO names
(gpio15/16 rather than the proper detect names).

> One solution could be to just remove the support from snd-aoa and
> continue using snd-powermac. Now it's difficult because of the
> auto-loading.

Yeah, not really ... I wrote aoa, so I guess I'm biased, but really
snd-powermac is quite a mess.

johannes

^ permalink raw reply

* Re: PowerMac G4 (Digital Audio)
From: Risto Suominen @ 2010-12-07 11:41 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev
In-Reply-To: <1291721424.3607.19.camel@jlt3.sipsolutions.net>

2010/12/7, Johannes Berg <johannes@sipsolutions.net>:
>
> Right, normally we don't need any special handling since the DT has the
> right names ... this machine just needs an override for the GPIO names
> (gpio15/16 rather than the proper detect names).
>
And setting the active-state manually to 1.

Risto

^ permalink raw reply

* Re: PowerMac G4 (Digital Audio)
From: Johannes Berg @ 2010-12-07 12:09 UTC (permalink / raw)
  To: Risto Suominen; +Cc: linuxppc-dev
In-Reply-To: <AANLkTimG7FLAxPmcXF53VpDS37DrKi=KnKDexF3zEoQ3@mail.gmail.com>

On Tue, 2010-12-07 at 13:41 +0200, Risto Suominen wrote:
> 2010/12/7, Johannes Berg <johannes@sipsolutions.net>:
> >
> > Right, normally we don't need any special handling since the DT has the
> > right names ... this machine just needs an override for the GPIO names
> > (gpio15/16 rather than the proper detect names).
> >
> And setting the active-state manually to 1.

Yes, I guess, it doesn't look like they have the typical property in the
DT for these.

johannes

^ permalink raw reply

* Re: Getting the IRQ number (Was: Basic driver devel questions ?)
From: Guillaume Dargaud @ 2010-12-07 12:46 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1291613340.11896.216.camel@concordia>

Michael, 
in your example, when is foo_driver_probe() actually called ?
It's not being called when I do an insmod.
-- 
Guillaume Dargaud
http://www.gdargaud.net/

^ permalink raw reply

* Re: Run 'usermode-agent' cause kernel panic on Powerpc
From: Dave Kleikamp @ 2010-12-07 14:04 UTC (permalink / raw)
  To: xufeng zhang; +Cc: Linuxppc-dev
In-Reply-To: <4CFDD8C5.70905@windriver.com>

On Tue, 2010-12-07 at 14:48 +0800, xufeng zhang wrote:
> Hi Dave,
> 
> I have a question with the below patch you made before:
> ----------------------------------------
> powerpc/booke: Add support for advanced debug registers
> 
> From: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
> 
> Based on patches originally written by Torez Smith.
> -----------------------------------------
> 
> I meet a kernel panic problem while running 'usermode-agent' on PowerPC
> ----------------------------------------
> Oops: Exception in kernel mode, sig: 5 [#1]
> PREEMPT LTT NESTING LEVEL : 0
> MPC8536 DS
> last sysfs file: 
> /sys/devices/f3000000.soc/f3003100.i2c/i2c-1/i2c-dev/i2c-1/dev
> Modules linked in:
> NIP: c00081a0 LR: c03a9560 CTR: c003547c
> REGS: ef11bf10 TRAP: 2002   Not tainted  (2.6.34.6-WR4.0.0.0_standard)
> MSR: 00021000 <ME,CE>  CR: 44000624  XER: 00000000
> TASK = efc1de00[752] 'usermode-agent' THREAD: ef63e000
> GPR00: cc00cc00 ef63fe60 efc1de00 efc1de00 efc1f700 c04c8000 00258560 
> ffffffff
> GPR08: ffda8a00 40000000 00001fda c0500000 49eaebbd 1008b654 3ff8a900 
> 00000000
> GPR16: 00000000 eed84c40 c03b4570 c04ee4d0 c04ca870 ef63e03c 00000000 
> 00000000
> GPR24: c04f7ee8 c04ee4c0 00000004 c04ca440 ef63e000 efc1f700 c04ca440 
> efc1de00
> NIP [c00081a0] __switch_to+0xac/0x104
> LR [c03a9560] schedule+0x20c/0x3f4
> Call Trace:
> [ef63fe60] [efc1f700] 0xefc1f700 (unreliable)
> [ef63fe70] [c03a9560] schedule+0x20c/0x3f4
> [ef63fec0] [c00429e0] do_wait+0x1a4/0x278
> [ef63fef0] [c0042b44] sys_wait4+0x90/0xf8
> [ef63ff40] [c00106d4] ret_from_syscall+0x0/0x4
> ------------------------------------------
> 
> Actually, this problem is caused by enabling On Chip Debugging, when On
> Chip Debugging is enabled, we enable MSR_DE as below:
> #define MSR_KERNEL      (MSR_ME|MSR_RI|MSR_CE|MSR_DE)
> 
> If I comment out "mtspr(SPRN_DBCR0, thread->dbcr0);" in 
> prime_debug_regs() function,
> then it will be ok.
> 
> Here is my analysis for this problem:
> Run 'usermode-agent' application will set Internal Debug Mode(IDM) and
> Instruction Complete Debug Event(ICMP)flags for thread.
> As MSR_DE is enabled, when execute context switching in prime_debug_regs(),
> thread->dbcr0 would write to SPRN_DBCR0 register.
> So this will enable Instruction Complete Debug Event interrupt, and it 
> will cause a kernel-mode
> exception right now, it will be handled in native_DebugException(), then 
> kernel detected
> this exception not happens in user-mode, lastly kernel call die() and 
> kill current process.
> 
> So my question is could I just comment out "mtspr(SPRN_DBCR0, 
> thread->dbcr0);" in prime_debug_regs()?
> I'm sure whether or not it will impose a bad impact on debugging.

I believe it would have such an impact.  I don't see that user-mode
debugging would be enabled at all.

Maybe something like this untested patch:

diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 84906d3..0e7d1cf 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -323,6 +323,13 @@ static void set_debug_reg_defaults(struct thread_struct *thread)
 
 static void prime_debug_regs(struct thread_struct *thread)
 {
+	/*
+	 * If we're setting up debug events for user space, make sure they
+	 * don't fire in kernel space before we get to user space
+	 */
+	if (thread->dbcr0 & DBCR0_IDM)
+		mtmsr(mfmsr() & ~MSR_DE);
+
 	mtspr(SPRN_IAC1, thread->iac1);
 	mtspr(SPRN_IAC2, thread->iac2);
 #if CONFIG_PPC_ADV_DEBUG_IACS > 2

-- 
Dave Kleikamp
IBM Linux Technology Center

^ permalink raw reply related

* Re: MPC831x (and others?) NAND erase performance improvements
From: Mark Mason @ 2010-12-07 18:23 UTC (permalink / raw)
  To: David Laight; +Cc: linuxppc-dev
In-Reply-To: <AE90C24D6B3A694183C094C60CF0A2F6D8ABEB@saturn3.aculab.com>

David Laight <David.Laight@ACULAB.COM> wrote:

> > The problem cropped up when there was a lot of traffic to the NAND
> > (Samsung K9WAGU08U1B-PIB0), with the NAND being on the LBC along with
> > a video chip that needed constant and prompt attention.
> > 
> > What I would see is that, as the writes happened, the erases would
> > wind up batched and issued all at once, such that frequently 400-700
> > erases were issued in rapid succession with a 1ms LBC BUSY cycle per
> > erase.
> 
> Are those just the reads of the status register polling to
> determine when the sector erase has completed ?

No, it's not, since it isn't polling the status register.  It's using
a hardware line from the NAND to indicate that the NAND is busy.  That
one hardware line is shared between all devices on the bus, so if one
device says it's busy then all bus traffic stops until the NAND
deasserts the busy line.

^ permalink raw reply

* Re: [PATCH] drivers: char: hvc: add arm JTAG DCC console support
From: Stephen Boyd @ 2010-12-07 19:10 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Randy Dunlap, Daniel Walker, Mike Frysinger, Arnd Bergmann,
	Nicolas Pitre, linux-arm-msm, Greg Kroah-Hartman, linux-kernel,
	FUJITA Tomonori, Andrew Morton, linuxppc-dev, Alan Cox
In-Reply-To: <4CF6AE04.6030201@codeaurora.org>

On 12/01/2010 12:20 PM, Stephen Boyd wrote:
> Definitely for TX since it seems like a redundant loop, but I agree RX
> code has changed. Instead of
>
> If RX buffer full
> Poll for RX buffer full
> Read character from RX buffer
>
> we would have
>
> If RX buffer full
> Read character from RX buffer
>
> which doesn't seem all that different assuming the RX buffer doesn't go
> from full to empty between the If and Poll steps. Hopefully Tony knows more.
>

Tony, any thoughts?

-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

^ permalink raw reply

* Re: MPC831x (and others?) NAND erase performance improvements
From: Scott Wood @ 2010-12-07 20:51 UTC (permalink / raw)
  To: Mark Mason; +Cc: linuxppc-dev
In-Reply-To: <20101207031554.GA12731@postdiluvian.org>

On Mon, 6 Dec 2010 22:15:54 -0500
Mark Mason <mason@postdiluvian.org> wrote:

> A few months ago I ran into some performance problems involving
> UBI/NAND erases holding other devices off the LBC on an MPC8315.  I
> found a solution for this, which worked well, at least with the
> hardware I was working with.  I suspect the same problem affects other
> PPCs, probably including multicore devices, and maybe other
> architectures as well.
> 
> I don't have experience with similar NAND controllers on other
> devices, so I'd like to explain what I found and see if someone who's
> more familiar with the family and/or driver can tell if this is
> useful.
> 
> The problem cropped up when there was a lot of traffic to the NAND
> (Samsung K9WAGU08U1B-PIB0), with the NAND being on the LBC along with
> a video chip that needed constant and prompt attention.

If you attach NAND to the LBC, you should not attach anything else to
it which is latency-sensitive.

> What I found, though, was that the NAND did not inherently assert BUSY
> as part of the erase - BUSY was asserted because the driver polled for
> the status (NAND_CMD_STATUS).  If the status poll was delayed for the
> duration of the erase then the MPC could talk to the video chip while
> the erase was in progress.  At the end of the 1ms delay I would then
> poll for status, which would complete effectively immediately.

That's what we originially did.  The problem is that during this
interval the NAND chip will be driving the busy pin, which corrupts
other LBC transactions.

Newer chips have this added text in their reference manuals under "NAND
Flash Block Erase Command Sequence Example":

  Note that operations specified by OP3 and OP4 (status read) should
  never be skipped while erasing a NAND Flash device, because, in case
  that happens, contention may arise on LGPL4.  A possible case is that
  the next transaction from eLBC may try to use that pin as an output
  and since the NAND Flash device might already be driving it,
  contention will occur.  In case OP3 and OP4 operations are skipped,
  it may also happen that a new command is issued to the NAND Flash
  device even when the device has not yet finished processing the
  previous request.  This may also result in unpredictable behavior.


> Here's a code snippet from 2.6.37, with some comments I added.
> drivers/mtd/nand/fsl_elbc_nand.c - fsl_elbc_cmdfunc():
> 
>   /* ERASE2 uses the block and page address from ERASE1 */
>   case NAND_CMD_ERASE2:
>     dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
> 
>     out_be32(&lbc->fir,
>        (FIR_OP_CM0 << FIR_OP0_SHIFT) |  /* Execute CMD0 (ERASE1).           */
>        (FIR_OP_PA  << FIR_OP1_SHIFT) |  /* Issue block and page address.    */
>        (FIR_OP_CM2 << FIR_OP2_SHIFT) |  /* Execute CMD2 (ERASE2).           */
>            /* (delay needed here - this is where the erase happens) */
>        (FIR_OP_CW1 << FIR_OP3_SHIFT) |  /* Wait for LFRB (BUSY) to deassert */
>                                         /* then issue CW1 (read status).    */
>        (FIR_OP_RS  << FIR_OP4_SHIFT));  /* Read one byte.                   */
> 
>     out_be32(&lbc->fcr,
>        (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |  /* 0x60 */
>        (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |  /* 0x70 */
>        (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));  /* 0xD0 */
> 
>     out_be32(&lbc->fbcr, 0);
>     elbc_fcm_ctrl->read_bytes = 0;
>     elbc_fcm_ctrl->use_mdr = 1;
> 
>     fsl_elbc_run_command(mtd);
>     return;
> 
> What I did was to issue two commands with fsl_elbc_run_command(), with
> a 1ms sleep in between (a tightloop delay worked almost as well, the
> important part was having 1ms between the erase and the status poll).
> The first command did the FIR_OP_CM0 (NAND_CMD_ERASE1), FIR_OP_PA, and
> FIR_OP_CM2 (NAND_CMD_ERASE2).  The second did the FIR_OP_CW1
> (NAND_CMD_STATUS) and FIR_OP_RS.

So essentially, you reverted commit
476459a6cf46d20ec73d9b211f3894ced5f9871e

:-)

Except for the 1ms delay.

> I know almost nothing at all about the scheduler, but I'm pretty sure
> that this behavior would cause the scheduler to think the video thread
> was a CPU hog, since the video thread was running for 1ms for every
> 20us that the UBI BGT ran, which would cause the scheduler to unfairly
> prefer the UBI BGT.  I initially tried to address this problem with
> thread priorities, but the unfortunate reality was that either the
> NAND writes could fall behind or the video chip could fall behind, and
> there wasn't spare bandwidth to allow either.

If you set a realtime priority and have preemption enabled, you should
be able to avoid being delayed by more than one NAND transaction, until
the realtime thread sleeps.  Be careful to ensure that it does sleep
enough for other things to run.

-Scott

^ permalink raw reply

* Re: MPC831x (and others?) NAND erase performance improvements
From: Mark Mason @ 2010-12-07 23:24 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20101207145153.540da45a@udp111988uds.am.freescale.net>

Scott Wood <scottwood@freescale.com> wrote:

> On Mon, 6 Dec 2010 22:15:54 -0500
> Mark Mason <mason@postdiluvian.org> wrote:
> 
> > A few months ago I ran into some performance problems involving
> > UBI/NAND erases holding other devices off the LBC on an MPC8315.  I
> > found a solution for this, which worked well, at least with the
> > hardware I was working with.  I suspect the same problem affects other
> > PPCs, probably including multicore devices, and maybe other
> > architectures as well.
> > 
> > I don't have experience with similar NAND controllers on other
> > devices, so I'd like to explain what I found and see if someone who's
> > more familiar with the family and/or driver can tell if this is
> > useful.
> > 
> > The problem cropped up when there was a lot of traffic to the NAND
> > (Samsung K9WAGU08U1B-PIB0), with the NAND being on the LBC along with
> > a video chip that needed constant and prompt attention.
> 
> If you attach NAND to the LBC, you should not attach anything else
> to it which is latency-sensitive.

We found that out the hard way.

The 1ms latency wasn't a problem by itself, the real problem was that
the quantity of erases issued in a short time significantly decreased
the bandwidth available, and that the scheduler saw the video thread
use 1ms of CPU time even though it'd only done a couple hundred
nanoseconds worth of work.

> > What I found, though, was that the NAND did not inherently assert BUSY
> > as part of the erase - BUSY was asserted because the driver polled for
> > the status (NAND_CMD_STATUS).  If the status poll was delayed for the
> > duration of the erase then the MPC could talk to the video chip while
> > the erase was in progress.  At the end of the 1ms delay I would then
> > poll for status, which would complete effectively immediately.
> 
> That's what we originially did.  The problem is that during this
> interval the NAND chip will be driving the busy pin, which corrupts
> other LBC transactions.

This is not what we observed with our flash part.  For a page erase,
the NAND did not assert the busy pin until the status read was done.
This was confirmed with a logic analyzer, and taking advantage of this
behavior is the sole purpose of the change.

I don't think that this behavior is what's described in the Samsung
datasheet, but it is what our parts did.

I incorrectly said "polled for status" in my original post.  It did
not poll for status, it monitored the busy line from NAND and did a
single read from the status register.

> Newer chips have this added text in their reference manuals under
> "NAND Flash Block Erase Command Sequence Example":
> 
>   Note that operations specified by OP3 and OP4 (status read) should
>   never be skipped while erasing a NAND Flash device, because, in
>   case that happens, contention may arise on LGPL4.  A possible case
>   is that the next transaction from eLBC may try to use that pin as
>   an output and since the NAND Flash device might already be driving
>   it, contention will occur.  In case OP3 and OP4 operations are
>   skipped, it may also happen that a new command is issued to the
>   NAND Flash device even when the device has not yet finished
>   processing the previous request.  This may also result in
>   unpredictable behavior.

I would expect those operations to be mandatory.

> > Here's a code snippet from 2.6.37, with some comments I added.
> > drivers/mtd/nand/fsl_elbc_nand.c - fsl_elbc_cmdfunc():
> > 
> >   /* ERASE2 uses the block and page address from ERASE1 */
> >   case NAND_CMD_ERASE2:
> >     dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
> > 
> >     out_be32(&lbc->fir,
> >        (FIR_OP_CM0 << FIR_OP0_SHIFT) |  /* Execute CMD0 (ERASE1).           */
> >        (FIR_OP_PA  << FIR_OP1_SHIFT) |  /* Issue block and page address.    */
> >        (FIR_OP_CM2 << FIR_OP2_SHIFT) |  /* Execute CMD2 (ERASE2).           */
> >            /* (delay needed here - this is where the erase happens) */
> >        (FIR_OP_CW1 << FIR_OP3_SHIFT) |  /* Wait for LFRB (BUSY) to deassert */
> >                                         /* then issue CW1 (read status).    */
> >        (FIR_OP_RS  << FIR_OP4_SHIFT));  /* Read one byte.                   */
> > 
> >     out_be32(&lbc->fcr,
> >        (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |  /* 0x60 */
> >        (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |  /* 0x70 */
> >        (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));  /* 0xD0 */
> > 
> >     out_be32(&lbc->fbcr, 0);
> >     elbc_fcm_ctrl->read_bytes = 0;
> >     elbc_fcm_ctrl->use_mdr = 1;
> > 
> >     fsl_elbc_run_command(mtd);
> >     return;
> > 
> > What I did was to issue two commands with fsl_elbc_run_command(), with
> > a 1ms sleep in between (a tightloop delay worked almost as well, the
> > important part was having 1ms between the erase and the status poll).
> > The first command did the FIR_OP_CM0 (NAND_CMD_ERASE1), FIR_OP_PA, and
> > FIR_OP_CM2 (NAND_CMD_ERASE2).  The second did the FIR_OP_CW1
> > (NAND_CMD_STATUS) and FIR_OP_RS.
> 
> So essentially, you reverted commit
> 476459a6cf46d20ec73d9b211f3894ced5f9871e
> 
> :-)
> 
> Except for the 1ms delay.

Well then, the 1ms delay is the value-add!

> > I know almost nothing at all about the scheduler, but I'm pretty
> > sure that this behavior would cause the scheduler to think the
> > video thread was a CPU hog, since the video thread was running for
> > 1ms for every 20us that the UBI BGT ran, which would cause the
> > scheduler to unfairly prefer the UBI BGT.  I initially tried to
> > address this problem with thread priorities, but the unfortunate
> > reality was that either the NAND writes could fall behind or the
> > video chip could fall behind, and there wasn't spare bandwidth to
> > allow either.
> 
> If you set a realtime priority and have preemption enabled, you
> should be able to avoid being delayed by more than one NAND
> transaction, until the realtime thread sleeps.  Be careful to ensure
> that it does sleep enough for other things to run.

I tried that, but if the erases were held off enough to get the other
bus bandwidth we required then the NAND writes fell behind and the
kernel oom'd.

> -Scott

Thanks for reviewing it.  It wouldn't surprise me if this wasn't
useful in a more general case, but it made a big difference for us.

^ permalink raw reply

* [PATCH] powerpc: iommu: Add device name to iommu error printks
From: Anton Blanchard @ 2010-12-08  0:36 UTC (permalink / raw)
  To: benh, linuxppc-dev


Right now its difficult to see which device is running out of iommu space:

iommu_alloc failed, tbl c00000076e096660 vaddr c000000768806600 npages 1

Use dev_info() so we get the device name and location:

ipr 0000:00:01.0: iommu_alloc failed, tbl c00000076e096660 vaddr c000000768806600 npages 1

Signed-off-by: Anton Blanchard <anton@samba.org> 
---

Index: powerpc.git/arch/powerpc/kernel/iommu.c
===================================================================
--- powerpc.git.orig/arch/powerpc/kernel/iommu.c	2010-12-08 11:28:39.055483332 +1100
+++ powerpc.git/arch/powerpc/kernel/iommu.c	2010-12-08 11:30:54.569176185 +1100
@@ -311,8 +311,9 @@ int iommu_map_sg(struct device *dev, str
 		/* Handle failure */
 		if (unlikely(entry == DMA_ERROR_CODE)) {
 			if (printk_ratelimit())
-				printk(KERN_INFO "iommu_alloc failed, tbl %p vaddr %lx"
-				       " npages %lx\n", tbl, vaddr, npages);
+				dev_info(dev, "iommu_alloc failed, tbl %p "
+					 "vaddr %lx npages %lu\n", tbl, vaddr,
+					 npages);
 			goto failure;
 		}
 
@@ -579,9 +580,9 @@ dma_addr_t iommu_map_page(struct device
 					 attrs);
 		if (dma_handle == DMA_ERROR_CODE) {
 			if (printk_ratelimit())  {
-				printk(KERN_INFO "iommu_alloc failed, "
-						"tbl %p vaddr %p npages %d\n",
-						tbl, vaddr, npages);
+				dev_info(dev, "iommu_alloc failed, tbl %p "
+					 "vaddr %p npages %d\n", tbl, vaddr,
+					 npages);
 			}
 		} else
 			dma_handle |= (uaddr & ~IOMMU_PAGE_MASK);
@@ -627,7 +628,8 @@ void *iommu_alloc_coherent(struct device
 	 * the tce tables.
 	 */
 	if (order >= IOMAP_MAX_ORDER) {
-		printk("iommu_alloc_consistent size too large: 0x%lx\n", size);
+		dev_info(dev, "iommu_alloc_consistent size too large: 0x%lx\n",
+			 size);
 		return NULL;
 	}
 

^ permalink raw reply

* Re: Getting the IRQ number (Was: Basic driver devel questions ?)
From: Michael Ellerman @ 2010-12-08  0:50 UTC (permalink / raw)
  To: Guillaume Dargaud; +Cc: linuxppc-dev
In-Reply-To: <201012071346.13434.dargaud@lpsc.in2p3.fr>

[-- Attachment #1: Type: text/plain, Size: 575 bytes --]

On Tue, 2010-12-07 at 13:46 +0100, Guillaume Dargaud wrote:
> Michael, 
> in your example, when is foo_driver_probe() actually called ?
> It's not being called when I do an insmod.

It should be called when the driver core finds a device that matches
your match table.

When you register your driver the driver core will iterate over all
devices on the platform bus and if they match then it will call your
probe routine.

I assume you've update the compatible property in the match table, I'm
not sure what else could be causing it to not be called.

cheers

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox