[SPARC64]: Add irqtrace/stacktrace/lockdep support.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index d391d11..d41f66a 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -26,6 +26,14 @@
 	bool
 	default y
 
+config STACKTRACE_SUPPORT
+	bool
+	default y
+
+config LOCKDEP_SUPPORT
+	bool
+	default y
+
 config TIME_INTERPOLATION
 	bool
 	default y
diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug
index afe0a77..1f130f3 100644
--- a/arch/sparc64/Kconfig.debug
+++ b/arch/sparc64/Kconfig.debug
@@ -1,5 +1,9 @@
 menu "Kernel hacking"
 
+config TRACE_IRQFLAGS_SUPPORT
+	bool
+	default y
+
 source "lib/Kconfig.debug"
 
 config DEBUG_STACK_USAGE
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index e1eabeb..eff0c01 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -14,6 +14,7 @@
 		   power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
 		   visemul.o prom.o of_device.o
 
+obj-$(CONFIG_STACKTRACE) += stacktrace.o
 obj-$(CONFIG_PCI)	 += ebus.o isa.o pci_common.o pci_iommu.o \
 			    pci_psycho.o pci_sabre.o pci_schizo.o \
 			    pci_sun4v.o pci_sun4v_asm.o
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
index 6f28bec..c15a3ed 100644
--- a/arch/sparc64/kernel/entry.S
+++ b/arch/sparc64/kernel/entry.S
@@ -597,7 +597,12 @@
 1:	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
 
-2:	mov		%l4, %o1
+2:
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call	trace_hardirqs_off
+	 nop
+#endif
+	mov		%l4, %o1
 	mov		%l5, %o2
 	call		spitfire_access_error
 	 add		%sp, PTREGS_OFF, %o0
@@ -824,6 +829,10 @@
 	wrpr		%g0, 15, %pil
 	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	mov		0x0, %o0
 	call		cheetah_plus_parity_error
 	 add		%sp, PTREGS_OFF, %o1
@@ -855,6 +864,10 @@
 	wrpr		%g0, 15, %pil
 	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	mov		0x1, %o0
 	call		cheetah_plus_parity_error
 	 add		%sp, PTREGS_OFF, %o1
@@ -1183,6 +1196,10 @@
 	wrpr		%g0, 15, %pil
 	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	mov		%l4, %o1
 	mov		%l5, %o2
 	call		cheetah_fecc_handler
@@ -1211,6 +1228,10 @@
 	wrpr		%g0, 15, %pil
 	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	mov		%l4, %o1
 	mov		%l5, %o2
 	call		cheetah_cee_handler
@@ -1239,6 +1260,10 @@
 	wrpr		%g0, 15, %pil
 	ba,pt		%xcc, etrap_irq
 	 rd		%pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	mov		%l4, %o1
 	mov		%l5, %o2
 	call		cheetah_deferred_handler
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index c8e9dc9..03ffaf89 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -489,6 +489,14 @@
 	call	__bzero
 	 sub	%o1, %o0, %o1
 
+#ifdef CONFIG_LOCKDEP
+	/* We have this call this super early, as even prom_init can grab
+	 * spinlocks and thus call into the lockdep code.
+	 */
+	call	lockdep_init
+	 nop
+#endif
+
 	mov	%l6, %o1			! OpenPROM stack
 	call	prom_init
 	 mov	%l7, %o0			! OpenPROM cif handler
diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S
index 3522cd6..079d18a 100644
--- a/arch/sparc64/kernel/rtrap.S
+++ b/arch/sparc64/kernel/rtrap.S
@@ -165,14 +165,26 @@
 __handle_softirq_continue:
 rtrap_xcall:
 		sethi			%hi(0xf << 20), %l4
-		andcc			%l1, TSTATE_PRIV, %l3
 		and			%l1, %l4, %l4
+		andn			%l1, %l4, %l1
+		srl			%l4, 20, %l4
+#ifdef CONFIG_TRACE_IRQFLAGS
+		brnz,pn			%l4, rtrap_no_irq_enable
+		 nop
+		call			trace_hardirqs_on
+		 nop
+		wrpr			%l4, %pil
+rtrap_no_irq_enable:
+#endif
+		andcc			%l1, TSTATE_PRIV, %l3
 		bne,pn			%icc, to_kernel
-		 andn			%l1, %l4, %l1
+		 nop
 
 		/* We must hold IRQs off and atomically test schedule+signal
 		 * state, then hold them off all the way back to userspace.
-		 * If we are returning to kernel, none of this matters.
+		 * If we are returning to kernel, none of this matters.  Note
+		 * that we are disabling interrupts via PSTATE_IE, not using
+		 * %pil.
 		 *
 		 * If we do not do this, there is a window where we would do
 		 * the tests, later the signal/resched event arrives but we do
@@ -256,7 +268,6 @@
 
 		ld			[%sp + PTREGS_OFF + PT_V9_Y], %o3
 		wr			%o3, %g0, %y
-		srl			%l4, 20, %l4
 		wrpr			%l4, 0x0, %pil
 		wrpr			%g0, 0x1, %tl
 		wrpr			%l1, %g0, %tstate
@@ -374,8 +385,8 @@
 		 ldx			[%g6 + TI_FLAGS], %l5
 		andcc			%l5, _TIF_NEED_RESCHED, %g0
 		be,pt			%xcc, kern_fpucheck
-		 srl			%l4, 20, %l5
-		cmp			%l5, 0
+		 nop
+		cmp			%l4, 0
 		bne,pn			%xcc, kern_fpucheck
 		 sethi			%hi(PREEMPT_ACTIVE), %l6
 		stw			%l6, [%g6 + TI_PRE_COUNT]
diff --git a/arch/sparc64/kernel/stacktrace.c b/arch/sparc64/kernel/stacktrace.c
new file mode 100644
index 0000000..c4d15f2
--- /dev/null
+++ b/arch/sparc64/kernel/stacktrace.c
@@ -0,0 +1,41 @@
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/thread_info.h>
+#include <asm/ptrace.h>
+
+void save_stack_trace(struct stack_trace *trace, struct task_struct *task)
+{
+	unsigned long ksp, fp, thread_base;
+	struct thread_info *tp;
+
+	if (!task)
+		task = current;
+	tp = task_thread_info(task);
+	if (task == current) {
+		flushw_all();
+		__asm__ __volatile__(
+			"mov	%%fp, %0"
+			: "=r" (ksp)
+		);
+	} else
+		ksp = tp->ksp;
+
+	fp = ksp + STACK_BIAS;
+	thread_base = (unsigned long) tp;
+	do {
+		struct reg_window *rw;
+
+		/* Bogus frame pointer? */
+		if (fp < (thread_base + sizeof(struct thread_info)) ||
+		    fp >= (thread_base + THREAD_SIZE))
+			break;
+
+		rw = (struct reg_window *) fp;
+		if (trace->skip > 0)
+			trace->skip--;
+		else
+			trace->entries[trace->nr_entries++] = rw->ins[7];
+
+		fp = rw->ins[6] + STACK_BIAS;
+	} while (trace->nr_entries < trace->max_entries);
+}
diff --git a/arch/sparc64/kernel/sun4v_ivec.S b/arch/sparc64/kernel/sun4v_ivec.S
index 49703c3..405855d 100644
--- a/arch/sparc64/kernel/sun4v_ivec.S
+++ b/arch/sparc64/kernel/sun4v_ivec.S
@@ -190,7 +190,10 @@
 	mov	%g1, %g4
 	ba,pt	%xcc, etrap_irq
 	 rd	%pc, %g7
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	/* Log the event.  */
 	add	%sp, PTREGS_OFF, %o0
 	call	sun4v_resum_error
@@ -216,7 +219,10 @@
 	wrpr	%g0, 15, %pil
 	ba,pt	%xcc, etrap_irq
 	 rd	%pc, %g7
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	call	sun4v_resum_overflow
 	 add	%sp, PTREGS_OFF, %o0
 
@@ -295,7 +301,10 @@
 	mov	%g1, %g4
 	ba,pt	%xcc, etrap_irq
 	 rd	%pc, %g7
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	/* Log the event.  */
 	add	%sp, PTREGS_OFF, %o0
 	call	sun4v_nonresum_error
@@ -321,7 +330,10 @@
 	wrpr	%g0, 15, %pil
 	ba,pt	%xcc, etrap_irq
 	 rd	%pc, %g7
-
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	call	sun4v_nonresum_overflow
 	 add	%sp, PTREGS_OFF, %o0
 
diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S
index d70b60a..737c269 100644
--- a/arch/sparc64/mm/ultra.S
+++ b/arch/sparc64/mm/ultra.S
@@ -477,6 +477,10 @@
 	sethi		%hi(109f), %g7
 	b,pt		%xcc, etrap_irq
 109:	 or		%g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	call		smp_synchronize_tick_client
 	 nop
 	clr		%l6
@@ -508,6 +512,10 @@
 	sethi		%hi(109f), %g7
 	b,pt		%xcc, etrap_irq
 109:	 or		%g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+	call		trace_hardirqs_off
+	 nop
+#endif
 	call		__show_regs
 	 add		%sp, PTREGS_OFF, %o0
 	clr		%l6
diff --git a/include/asm-sparc64/irqflags.h b/include/asm-sparc64/irqflags.h
new file mode 100644
index 0000000..024fc54
--- /dev/null
+++ b/include/asm-sparc64/irqflags.h
@@ -0,0 +1,89 @@
+/*
+ * include/asm-sparc64/irqflags.h
+ *
+ * IRQ flags handling
+ *
+ * This file gets included from lowlevel asm headers too, to provide
+ * wrapped versions of the local_irq_*() APIs, based on the
+ * raw_local_irq_*() functions from the lowlevel headers.
+ */
+#ifndef _ASM_IRQFLAGS_H
+#define _ASM_IRQFLAGS_H
+
+#ifndef __ASSEMBLY__
+
+static inline unsigned long __raw_local_save_flags(void)
+{
+	unsigned long flags;
+
+	__asm__ __volatile__(
+		"rdpr	%%pil, %0"
+		: "=r" (flags)
+	);
+
+	return flags;
+}
+
+#define raw_local_save_flags(flags) \
+		do { (flags) = __raw_local_save_flags(); } while (0)
+
+static inline void raw_local_irq_restore(unsigned long flags)
+{
+	__asm__ __volatile__(
+		"wrpr	%0, %%pil"
+		: /* no output */
+		: "r" (flags)
+		: "memory"
+	);
+}
+
+static inline void raw_local_irq_disable(void)
+{
+	__asm__ __volatile__(
+		"wrpr	15, %%pil"
+		: /* no outputs */
+		: /* no inputs */
+		: "memory"
+	);
+}
+
+static inline void raw_local_irq_enable(void)
+{
+	__asm__ __volatile__(
+		"wrpr	0, %%pil"
+		: /* no outputs */
+		: /* no inputs */
+		: "memory"
+	);
+}
+
+static inline int raw_irqs_disabled_flags(unsigned long flags)
+{
+	return (flags > 0);
+}
+
+static inline int raw_irqs_disabled(void)
+{
+	unsigned long flags = __raw_local_save_flags();
+
+	return raw_irqs_disabled_flags(flags);
+}
+
+/*
+ * For spinlocks, etc:
+ */
+static inline unsigned long __raw_local_irq_save(void)
+{
+	unsigned long flags = __raw_local_save_flags();
+
+	raw_local_irq_disable();
+
+	return flags;
+}
+
+#define raw_local_irq_save(flags) \
+		do { (flags) = __raw_local_irq_save(); } while (0)
+
+#endif /* (__ASSEMBLY__) */
+
+#endif /* !(_ASM_IRQFLAGS_H) */
diff --git a/include/asm-sparc64/rwsem.h b/include/asm-sparc64/rwsem.h
index cef5e82..1294b7c 100644
--- a/include/asm-sparc64/rwsem.h
+++ b/include/asm-sparc64/rwsem.h
@@ -23,20 +23,33 @@
 	signed int count;
 	spinlock_t		wait_lock;
 	struct list_head	wait_list;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	struct lockdep_map	dep_map;
+#endif
 };
 
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
+#else
+# define __RWSEM_DEP_MAP_INIT(lockname)
+#endif
+
 #define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) }
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \
+  __RWSEM_DEP_MAP_INIT(name) }
 
 #define DECLARE_RWSEM(name) \
 	struct rw_semaphore name = __RWSEM_INITIALIZER(name)
 
-static __inline__ void init_rwsem(struct rw_semaphore *sem)
-{
-	sem->count = RWSEM_UNLOCKED_VALUE;
-	spin_lock_init(&sem->wait_lock);
-	INIT_LIST_HEAD(&sem->wait_list);
-}
+extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
+			 struct lock_class_key *key);
+
+#define init_rwsem(sem)						\
+do {								\
+	static struct lock_class_key __key;			\
+								\
+	__init_rwsem((sem), #sem, &__key);			\
+} while (0)
 
 extern void __down_read(struct rw_semaphore *sem);
 extern int __down_read_trylock(struct rw_semaphore *sem);
@@ -46,6 +59,11 @@
 extern void __up_write(struct rw_semaphore *sem);
 extern void __downgrade_write(struct rw_semaphore *sem);
 
+static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
+{
+	__down_write(sem);
+}
+
 static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
 {
 	return atomic_add_return(delta, (atomic_t *)(&sem->count));
diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h
index a8b7432..32281ac 100644
--- a/include/asm-sparc64/system.h
+++ b/include/asm-sparc64/system.h
@@ -7,6 +7,9 @@
 #include <asm/visasm.h>
 
 #ifndef __ASSEMBLY__
+
+#include <linux/irqflags.h>
+
 /*
  * Sparc (general) CPU types
  */
@@ -72,52 +75,6 @@
 
 #endif
 
-#define setipl(__new_ipl) \
-	__asm__ __volatile__("wrpr	%0, %%pil"  : : "r" (__new_ipl) : "memory")
-
-#define local_irq_disable() \
-	__asm__ __volatile__("wrpr	15, %%pil" : : : "memory")
-
-#define local_irq_enable() \
-	__asm__ __volatile__("wrpr	0, %%pil" : : : "memory")
-
-#define getipl() \
-({ unsigned long retval; __asm__ __volatile__("rdpr	%%pil, %0" : "=r" (retval)); retval; })
-
-#define swap_pil(__new_pil) \
-({	unsigned long retval; \
-	__asm__ __volatile__("rdpr	%%pil, %0\n\t" \
-			     "wrpr	%1, %%pil" \
-			     : "=&r" (retval) \
-			     : "r" (__new_pil) \
-			     : "memory"); \
-	retval; \
-})
-
-#define read_pil_and_cli() \
-({	unsigned long retval; \
-	__asm__ __volatile__("rdpr	%%pil, %0\n\t" \
-			     "wrpr	15, %%pil" \
-			     : "=r" (retval) \
-			     : : "memory"); \
-	retval; \
-})
-
-#define local_save_flags(flags)		((flags) = getipl())
-#define local_irq_save(flags)		((flags) = read_pil_and_cli())
-#define local_irq_restore(flags)		setipl((flags))
-
-/* On sparc64 IRQ flags are the PIL register.  A value of zero
- * means all interrupt levels are enabled, any other value means
- * only IRQ levels greater than that value will be received.
- * Consequently this means that the lowest IRQ level is one.
- */
-#define irqs_disabled()		\
-({	unsigned long flags;	\
-	local_save_flags(flags);\
-	(flags > 0);		\
-})
-
 #define nop() 		__asm__ __volatile__ ("nop")
 
 #define read_barrier_depends()		do { } while(0)
diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h
index f235260..c2a16e1 100644
--- a/include/asm-sparc64/ttable.h
+++ b/include/asm-sparc64/ttable.h
@@ -137,10 +137,49 @@
 #endif
 #define BREAKPOINT_TRAP TRAP(breakpoint_trap)
 
+#ifdef CONFIG_TRACE_IRQFLAGS
+
 #define TRAP_IRQ(routine, level)			\
 	rdpr	%pil, %g2;				\
 	wrpr	%g0, 15, %pil;				\
-	b,pt	%xcc, etrap_irq;			\
+	sethi	%hi(1f-4), %g7;				\
+	ba,pt	%xcc, etrap_irq;			\
+	 or	%g7, %lo(1f-4), %g7;			\
+	nop;						\
+	nop;						\
+	nop;						\
+	.subsection	2;				\
+1:	call	trace_hardirqs_off;			\
+	 nop;						\
+	mov	level, %o0;				\
+	call	routine;				\
+	 add	%sp, PTREGS_OFF, %o1;			\
+	ba,a,pt	%xcc, rtrap_irq;			\
+	.previous;
+
+#define TICK_SMP_IRQ					\
+	rdpr	%pil, %g2;				\
+	wrpr	%g0, 15, %pil;				\
+	sethi	%hi(1f-4), %g7;				\
+	ba,pt	%xcc, etrap_irq;			\
+	 or	%g7, %lo(1f-4), %g7;			\
+	nop;						\
+	nop;						\
+	nop;						\
+	.subsection	2;				\
+1:	call	trace_hardirqs_off;			\
+	 nop;						\
+	call	smp_percpu_timer_interrupt;		\
+	 add	%sp, PTREGS_OFF, %o0;			\
+	ba,a,pt	%xcc, rtrap_irq;			\
+	.previous;
+
+#else
+
+#define TRAP_IRQ(routine, level)			\
+	rdpr	%pil, %g2;				\
+	wrpr	%g0, 15, %pil;				\
+	ba,pt	%xcc, etrap_irq;			\
 	 rd	%pc, %g7;				\
 	mov	level, %o0;				\
 	call	routine;				\
@@ -151,12 +190,14 @@
 	rdpr	%pil, %g2;				\
 	wrpr	%g0, 15, %pil;				\
 	sethi	%hi(109f), %g7;				\
-	b,pt	%xcc, etrap_irq;			\
+	ba,pt	%xcc, etrap_irq;			\
 109:	 or	%g7, %lo(109b), %g7;			\
 	call	smp_percpu_timer_interrupt;		\
 	 add	%sp, PTREGS_OFF, %o0;			\
 	ba,a,pt	%xcc, rtrap_irq;
 
+#endif
+
 #define TRAP_IVEC TRAP_NOSAVE(do_ivec)
 
 #define BTRAP(lvl) TRAP_ARG(bad_trap, lvl)