forked from Minki/linux
sparc64: Add global PMU register dumping via sysrq.
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
08280e6c4c
commit
916ca14aaf
@ -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]
|
||||||
|
|
||||||
|
@ -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; \
|
||||||
|
@ -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) */
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
return register_sysrq_key('y', &sparc_globalreg_op);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
core_initcall(sparc_globreg_init);
|
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)
|
||||||
|
{
|
||||||
|
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_sysrq_init);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -852,6 +852,8 @@ extern unsigned long xcall_flush_tlb_mm;
|
|||||||
extern unsigned long xcall_flush_tlb_pending;
|
extern unsigned long xcall_flush_tlb_pending;
|
||||||
extern unsigned long xcall_flush_tlb_kernel_range;
|
extern unsigned long xcall_flush_tlb_kernel_range;
|
||||||
extern unsigned long xcall_fetch_glob_regs;
|
extern unsigned long xcall_fetch_glob_regs;
|
||||||
|
extern unsigned long xcall_fetch_glob_pmu;
|
||||||
|
extern unsigned long xcall_fetch_glob_pmu_n4;
|
||||||
extern unsigned long xcall_receive_signal;
|
extern unsigned long xcall_receive_signal;
|
||||||
extern unsigned long xcall_new_mmu_context_version;
|
extern unsigned long xcall_new_mmu_context_version;
|
||||||
#ifdef CONFIG_KGDB
|
#ifdef CONFIG_KGDB
|
||||||
@ -1000,6 +1002,15 @@ void smp_fetch_global_regs(void)
|
|||||||
smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
|
smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void smp_fetch_global_pmu(void)
|
||||||
|
{
|
||||||
|
if (tlb_type == hypervisor &&
|
||||||
|
sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
|
||||||
|
smp_cross_call(&xcall_fetch_glob_pmu_n4, 0, 0, 0);
|
||||||
|
else
|
||||||
|
smp_cross_call(&xcall_fetch_glob_pmu, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* We know that the window frames of the user have been flushed
|
/* We know that the window frames of the user have been flushed
|
||||||
* to the stack before we get here because all callers of us
|
* to the stack before we get here because all callers of us
|
||||||
* are flush_tlb_*() routines, and these run after flush_cache_*()
|
* are flush_tlb_*() routines, and these run after flush_cache_*()
|
||||||
|
@ -481,8 +481,8 @@ xcall_sync_tick:
|
|||||||
|
|
||||||
.globl xcall_fetch_glob_regs
|
.globl xcall_fetch_glob_regs
|
||||||
xcall_fetch_glob_regs:
|
xcall_fetch_glob_regs:
|
||||||
sethi %hi(global_reg_snapshot), %g1
|
sethi %hi(global_cpu_snapshot), %g1
|
||||||
or %g1, %lo(global_reg_snapshot), %g1
|
or %g1, %lo(global_cpu_snapshot), %g1
|
||||||
__GET_CPUID(%g2)
|
__GET_CPUID(%g2)
|
||||||
sllx %g2, 6, %g3
|
sllx %g2, 6, %g3
|
||||||
add %g1, %g3, %g1
|
add %g1, %g3, %g1
|
||||||
@ -509,6 +509,66 @@ xcall_fetch_glob_regs:
|
|||||||
stx %g3, [%g1 + GR_SNAP_THREAD]
|
stx %g3, [%g1 + GR_SNAP_THREAD]
|
||||||
retry
|
retry
|
||||||
|
|
||||||
|
.globl xcall_fetch_glob_pmu
|
||||||
|
xcall_fetch_glob_pmu:
|
||||||
|
sethi %hi(global_cpu_snapshot), %g1
|
||||||
|
or %g1, %lo(global_cpu_snapshot), %g1
|
||||||
|
__GET_CPUID(%g2)
|
||||||
|
sllx %g2, 6, %g3
|
||||||
|
add %g1, %g3, %g1
|
||||||
|
rd %pic, %g7
|
||||||
|
stx %g7, [%g1 + (4 * 8)]
|
||||||
|
rd %pcr, %g7
|
||||||
|
stx %g7, [%g1 + (0 * 8)]
|
||||||
|
retry
|
||||||
|
|
||||||
|
.globl xcall_fetch_glob_pmu_n4
|
||||||
|
xcall_fetch_glob_pmu_n4:
|
||||||
|
sethi %hi(global_cpu_snapshot), %g1
|
||||||
|
or %g1, %lo(global_cpu_snapshot), %g1
|
||||||
|
__GET_CPUID(%g2)
|
||||||
|
sllx %g2, 6, %g3
|
||||||
|
add %g1, %g3, %g1
|
||||||
|
|
||||||
|
ldxa [%g0] ASI_PIC, %g7
|
||||||
|
stx %g7, [%g1 + (4 * 8)]
|
||||||
|
mov 0x08, %g3
|
||||||
|
ldxa [%g3] ASI_PIC, %g7
|
||||||
|
stx %g7, [%g1 + (5 * 8)]
|
||||||
|
mov 0x10, %g3
|
||||||
|
ldxa [%g3] ASI_PIC, %g7
|
||||||
|
stx %g7, [%g1 + (6 * 8)]
|
||||||
|
mov 0x18, %g3
|
||||||
|
ldxa [%g3] ASI_PIC, %g7
|
||||||
|
stx %g7, [%g1 + (7 * 8)]
|
||||||
|
|
||||||
|
mov %o0, %g2
|
||||||
|
mov %o1, %g3
|
||||||
|
mov %o5, %g7
|
||||||
|
|
||||||
|
mov HV_FAST_VT_GET_PERFREG, %o5
|
||||||
|
mov 3, %o0
|
||||||
|
ta HV_FAST_TRAP
|
||||||
|
stx %o1, [%g1 + (3 * 8)]
|
||||||
|
mov HV_FAST_VT_GET_PERFREG, %o5
|
||||||
|
mov 2, %o0
|
||||||
|
ta HV_FAST_TRAP
|
||||||
|
stx %o1, [%g1 + (2 * 8)]
|
||||||
|
mov HV_FAST_VT_GET_PERFREG, %o5
|
||||||
|
mov 1, %o0
|
||||||
|
ta HV_FAST_TRAP
|
||||||
|
stx %o1, [%g1 + (1 * 8)]
|
||||||
|
mov HV_FAST_VT_GET_PERFREG, %o5
|
||||||
|
mov 0, %o0
|
||||||
|
ta HV_FAST_TRAP
|
||||||
|
stx %o1, [%g1 + (0 * 8)]
|
||||||
|
|
||||||
|
mov %g2, %o0
|
||||||
|
mov %g3, %o1
|
||||||
|
mov %g7, %o5
|
||||||
|
|
||||||
|
retry
|
||||||
|
|
||||||
#ifdef DCACHE_ALIASING_POSSIBLE
|
#ifdef DCACHE_ALIASING_POSSIBLE
|
||||||
.align 32
|
.align 32
|
||||||
.globl xcall_flush_dcache_page_cheetah
|
.globl xcall_flush_dcache_page_cheetah
|
||||||
|
@ -452,6 +452,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
|
|||||||
NULL, /* v */
|
NULL, /* v */
|
||||||
&sysrq_showstate_blocked_op, /* w */
|
&sysrq_showstate_blocked_op, /* w */
|
||||||
/* x: May be registered on ppc/powerpc for xmon */
|
/* x: May be registered on ppc/powerpc for xmon */
|
||||||
|
/* x: May be registered on sparc64 for global PMU dump */
|
||||||
NULL, /* x */
|
NULL, /* x */
|
||||||
/* y: May be registered on sparc64 for global register dump */
|
/* y: May be registered on sparc64 for global register dump */
|
||||||
NULL, /* y */
|
NULL, /* y */
|
||||||
|
Loading…
Reference in New Issue
Block a user