Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ccbfddb7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sparc fixes from David Miller:
 "Two sparc64 perf bug fixes and add a sysrq facility so I can diagnose
  these kinds of problems more quickly in the future."

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc:
  sparc64: Fix bit twiddling in sparc_pmu_enable_event().
  sparc64: Add global PMU register dumping via sysrq.
  sparc64: Like x86 we should check current->mm during perf backtrace generation.
parents 8d2b6b3a e793d8c6
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -116,6 +116,7 @@ On all - write a character to /proc/sysrq-trigger. e.g.:
'w'	- Dumps tasks that are in uninterruptable (blocked) state.
'w'	- Dumps tasks that are in uninterruptable (blocked) state.


'x'	- Used by xmon interface on ppc/powerpc platforms.
'x'	- Used by xmon interface on ppc/powerpc platforms.
          Show global PMU Registers on sparc64.


'y'	- Show global CPU Registers [SPARC-64 specific]
'y'	- Show global CPU Registers [SPARC-64 specific]


+12 −1
Original line number Original line Diff line number Diff line
@@ -42,7 +42,18 @@ struct global_reg_snapshot {
	struct thread_info	*thread;
	struct thread_info	*thread;
	unsigned long		pad1;
	unsigned long		pad1;
};
};
extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS];

struct global_pmu_snapshot {
	unsigned long		pcr[4];
	unsigned long		pic[4];
};

union global_cpu_snapshot {
	struct global_reg_snapshot	reg;
	struct global_pmu_snapshot	pmu;
};

extern union global_cpu_snapshot global_cpu_snapshot[NR_CPUS];


#define force_successful_syscall_return()	    \
#define force_successful_syscall_return()	    \
do {	current_thread_info()->syscall_noerror = 1; \
do {	current_thread_info()->syscall_noerror = 1; \
+2 −0
Original line number Original line Diff line number Diff line
@@ -48,6 +48,7 @@ extern void smp_fill_in_sib_core_maps(void);
extern void cpu_play_dead(void);
extern void cpu_play_dead(void);


extern void smp_fetch_global_regs(void);
extern void smp_fetch_global_regs(void);
extern void smp_fetch_global_pmu(void);


struct seq_file;
struct seq_file;
void smp_bogo(struct seq_file *);
void smp_bogo(struct seq_file *);
@@ -65,6 +66,7 @@ extern void __cpu_die(unsigned int cpu);
#define hard_smp_processor_id()		0
#define hard_smp_processor_id()		0
#define smp_fill_in_sib_core_maps() do { } while (0)
#define smp_fill_in_sib_core_maps() do { } while (0)
#define smp_fetch_global_regs() do { } while (0)
#define smp_fetch_global_regs() do { } while (0)
#define smp_fetch_global_pmu() do { } while (0)


#endif /* !(CONFIG_SMP) */
#endif /* !(CONFIG_SMP) */


+9 −6
Original line number Original line Diff line number Diff line
@@ -817,15 +817,17 @@ static u64 nop_for_index(int idx)


static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
{
{
	u64 val, mask = mask_for_index(idx);
	u64 enc, val, mask = mask_for_index(idx);
	int pcr_index = 0;
	int pcr_index = 0;


	if (sparc_pmu->num_pcrs > 1)
	if (sparc_pmu->num_pcrs > 1)
		pcr_index = idx;
		pcr_index = idx;


	enc = perf_event_get_enc(cpuc->events[idx]);

	val = cpuc->pcr[pcr_index];
	val = cpuc->pcr[pcr_index];
	val &= ~mask;
	val &= ~mask;
	val |= hwc->config;
	val |= event_encoding(enc, idx);
	cpuc->pcr[pcr_index] = val;
	cpuc->pcr[pcr_index] = val;


	pcr_ops->write_pcr(pcr_index, cpuc->pcr[pcr_index]);
	pcr_ops->write_pcr(pcr_index, cpuc->pcr[pcr_index]);
@@ -1738,8 +1740,6 @@ static void perf_callchain_user_64(struct perf_callchain_entry *entry,
{
{
	unsigned long ufp;
	unsigned long ufp;


	perf_callchain_store(entry, regs->tpc);

	ufp = regs->u_regs[UREG_I6] + STACK_BIAS;
	ufp = regs->u_regs[UREG_I6] + STACK_BIAS;
	do {
	do {
		struct sparc_stackf *usf, sf;
		struct sparc_stackf *usf, sf;
@@ -1760,8 +1760,6 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,
{
{
	unsigned long ufp;
	unsigned long ufp;


	perf_callchain_store(entry, regs->tpc);

	ufp = regs->u_regs[UREG_I6] & 0xffffffffUL;
	ufp = regs->u_regs[UREG_I6] & 0xffffffffUL;
	do {
	do {
		struct sparc_stackf32 *usf, sf;
		struct sparc_stackf32 *usf, sf;
@@ -1780,6 +1778,11 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,
void
void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
{
	perf_callchain_store(entry, regs->tpc);

	if (!current->mm)
		return;

	flushw_user();
	flushw_user();
	if (test_thread_flag(TIF_32BIT))
	if (test_thread_flag(TIF_32BIT))
		perf_callchain_user_32(entry, regs);
		perf_callchain_user_32(entry, regs);
+100 −20
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/tick.h>
#include <linux/tick.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/cpu.h>
#include <linux/perf_event.h>
#include <linux/elfcore.h>
#include <linux/elfcore.h>
#include <linux/sysrq.h>
#include <linux/sysrq.h>
#include <linux/nmi.h>
#include <linux/nmi.h>
@@ -47,6 +48,7 @@
#include <asm/syscalls.h>
#include <asm/syscalls.h>
#include <asm/irq_regs.h>
#include <asm/irq_regs.h>
#include <asm/smp.h>
#include <asm/smp.h>
#include <asm/pcr.h>


#include "kstack.h"
#include "kstack.h"


@@ -204,18 +206,22 @@ void show_regs(struct pt_regs *regs)
	show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]);
	show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]);
}
}


struct global_reg_snapshot global_reg_snapshot[NR_CPUS];
union global_cpu_snapshot global_cpu_snapshot[NR_CPUS];
static DEFINE_SPINLOCK(global_reg_snapshot_lock);
static DEFINE_SPINLOCK(global_cpu_snapshot_lock);


static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
			      int this_cpu)
			      int this_cpu)
{
{
	struct global_reg_snapshot *rp;

	flushw_all();
	flushw_all();


	global_reg_snapshot[this_cpu].tstate = regs->tstate;
	rp = &global_cpu_snapshot[this_cpu].reg;
	global_reg_snapshot[this_cpu].tpc = regs->tpc;

	global_reg_snapshot[this_cpu].tnpc = regs->tnpc;
	rp->tstate = regs->tstate;
	global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7];
	rp->tpc = regs->tpc;
	rp->tnpc = regs->tnpc;
	rp->o7 = regs->u_regs[UREG_I7];


	if (regs->tstate & TSTATE_PRIV) {
	if (regs->tstate & TSTATE_PRIV) {
		struct reg_window *rw;
		struct reg_window *rw;
@@ -223,17 +229,17 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
		rw = (struct reg_window *)
		rw = (struct reg_window *)
			(regs->u_regs[UREG_FP] + STACK_BIAS);
			(regs->u_regs[UREG_FP] + STACK_BIAS);
		if (kstack_valid(tp, (unsigned long) rw)) {
		if (kstack_valid(tp, (unsigned long) rw)) {
			global_reg_snapshot[this_cpu].i7 = rw->ins[7];
			rp->i7 = rw->ins[7];
			rw = (struct reg_window *)
			rw = (struct reg_window *)
				(rw->ins[6] + STACK_BIAS);
				(rw->ins[6] + STACK_BIAS);
			if (kstack_valid(tp, (unsigned long) rw))
			if (kstack_valid(tp, (unsigned long) rw))
				global_reg_snapshot[this_cpu].rpc = rw->ins[7];
				rp->rpc = rw->ins[7];
		}
		}
	} else {
	} else {
		global_reg_snapshot[this_cpu].i7 = 0;
		rp->i7 = 0;
		global_reg_snapshot[this_cpu].rpc = 0;
		rp->rpc = 0;
	}
	}
	global_reg_snapshot[this_cpu].thread = tp;
	rp->thread = tp;
}
}


/* In order to avoid hangs we do not try to synchronize with the
/* In order to avoid hangs we do not try to synchronize with the
@@ -261,9 +267,9 @@ void arch_trigger_all_cpu_backtrace(void)
	if (!regs)
	if (!regs)
		regs = tp->kregs;
		regs = tp->kregs;


	spin_lock_irqsave(&global_reg_snapshot_lock, flags);
	spin_lock_irqsave(&global_cpu_snapshot_lock, flags);


	memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
	memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));


	this_cpu = raw_smp_processor_id();
	this_cpu = raw_smp_processor_id();


@@ -272,7 +278,7 @@ void arch_trigger_all_cpu_backtrace(void)
	smp_fetch_global_regs();
	smp_fetch_global_regs();


	for_each_online_cpu(cpu) {
	for_each_online_cpu(cpu) {
		struct global_reg_snapshot *gp = &global_reg_snapshot[cpu];
		struct global_reg_snapshot *gp = &global_cpu_snapshot[cpu].reg;


		__global_reg_poll(gp);
		__global_reg_poll(gp);


@@ -295,9 +301,9 @@ void arch_trigger_all_cpu_backtrace(void)
		}
		}
	}
	}


	memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
	memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));


	spin_unlock_irqrestore(&global_reg_snapshot_lock, flags);
	spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
}
}


#ifdef CONFIG_MAGIC_SYSRQ
#ifdef CONFIG_MAGIC_SYSRQ
@@ -309,16 +315,90 @@ static void sysrq_handle_globreg(int key)


static struct sysrq_key_op sparc_globalreg_op = {
static struct sysrq_key_op sparc_globalreg_op = {
	.handler	= sysrq_handle_globreg,
	.handler	= sysrq_handle_globreg,
	.help_msg	= "Globalregs",
	.help_msg	= "global-regs(Y)",
	.action_msg	= "Show Global CPU Regs",
	.action_msg	= "Show Global CPU Regs",
};
};


static int __init sparc_globreg_init(void)
static void __global_pmu_self(int this_cpu)
{
	struct global_pmu_snapshot *pp;
	int i, num;

	pp = &global_cpu_snapshot[this_cpu].pmu;

	num = 1;
	if (tlb_type == hypervisor &&
	    sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
		num = 4;

	for (i = 0; i < num; i++) {
		pp->pcr[i] = pcr_ops->read_pcr(i);
		pp->pic[i] = pcr_ops->read_pic(i);
	}
}

static void __global_pmu_poll(struct global_pmu_snapshot *pp)
{
	int limit = 0;

	while (!pp->pcr[0] && ++limit < 100) {
		barrier();
		udelay(1);
	}
}

static void pmu_snapshot_all_cpus(void)
{
{
	return register_sysrq_key('y', &sparc_globalreg_op);
	unsigned long flags;
	int this_cpu, cpu;

	spin_lock_irqsave(&global_cpu_snapshot_lock, flags);

	memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));

	this_cpu = raw_smp_processor_id();

	__global_pmu_self(this_cpu);

	smp_fetch_global_pmu();

	for_each_online_cpu(cpu) {
		struct global_pmu_snapshot *pp = &global_cpu_snapshot[cpu].pmu;

		__global_pmu_poll(pp);

		printk("%c CPU[%3d]: PCR[%08lx:%08lx:%08lx:%08lx] PIC[%08lx:%08lx:%08lx:%08lx]\n",
		       (cpu == this_cpu ? '*' : ' '), cpu,
		       pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3],
		       pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]);
	}

	memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));

	spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
}

static void sysrq_handle_globpmu(int key)
{
	pmu_snapshot_all_cpus();
}

static struct sysrq_key_op sparc_globalpmu_op = {
	.handler	= sysrq_handle_globpmu,
	.help_msg	= "global-pmu(X)",
	.action_msg	= "Show Global PMU Regs",
};

static int __init sparc_sysrq_init(void)
{
	int ret = register_sysrq_key('y', &sparc_globalreg_op);

	if (!ret)
		ret = register_sysrq_key('x', &sparc_globalpmu_op);
	return ret;
}
}


core_initcall(sparc_globreg_init);
core_initcall(sparc_sysrq_init);


#endif
#endif


Loading