Commit 916ca14aaf12a7191118adb51bb95e3c7866380d

Authored by David S. Miller
1 parent 08280e6c4c

sparc64: Add global PMU register dumping via sysrq.

Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 7 changed files with 189 additions and 23 deletions Side-by-side Diff

Documentation/sysrq.txt
... ... @@ -116,6 +116,7 @@
116 116 'w' - Dumps tasks that are in uninterruptable (blocked) state.
117 117  
118 118 'x' - Used by xmon interface on ppc/powerpc platforms.
  119 + Show global PMU Registers on sparc64.
119 120  
120 121 'y' - Show global CPU Registers [SPARC-64 specific]
121 122  
arch/sparc/include/asm/ptrace.h
... ... @@ -42,7 +42,18 @@
42 42 struct thread_info *thread;
43 43 unsigned long pad1;
44 44 };
45   -extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS];
  45 +
  46 +struct global_pmu_snapshot {
  47 + unsigned long pcr[4];
  48 + unsigned long pic[4];
  49 +};
  50 +
  51 +union global_cpu_snapshot {
  52 + struct global_reg_snapshot reg;
  53 + struct global_pmu_snapshot pmu;
  54 +};
  55 +
  56 +extern union global_cpu_snapshot global_cpu_snapshot[NR_CPUS];
46 57  
47 58 #define force_successful_syscall_return() \
48 59 do { current_thread_info()->syscall_noerror = 1; \
arch/sparc/include/asm/smp_64.h
... ... @@ -48,6 +48,7 @@
48 48 extern void cpu_play_dead(void);
49 49  
50 50 extern void smp_fetch_global_regs(void);
  51 +extern void smp_fetch_global_pmu(void);
51 52  
52 53 struct seq_file;
53 54 void smp_bogo(struct seq_file *);
... ... @@ -65,6 +66,7 @@
65 66 #define hard_smp_processor_id() 0
66 67 #define smp_fill_in_sib_core_maps() do { } while (0)
67 68 #define smp_fetch_global_regs() do { } while (0)
  69 +#define smp_fetch_global_pmu() do { } while (0)
68 70  
69 71 #endif /* !(CONFIG_SMP) */
70 72  
arch/sparc/kernel/process_64.c
... ... @@ -27,6 +27,7 @@
27 27 #include <linux/tick.h>
28 28 #include <linux/init.h>
29 29 #include <linux/cpu.h>
  30 +#include <linux/perf_event.h>
30 31 #include <linux/elfcore.h>
31 32 #include <linux/sysrq.h>
32 33 #include <linux/nmi.h>
... ... @@ -47,6 +48,7 @@
47 48 #include <asm/syscalls.h>
48 49 #include <asm/irq_regs.h>
49 50 #include <asm/smp.h>
  51 +#include <asm/pcr.h>
50 52  
51 53 #include "kstack.h"
52 54  
53 55  
54 56  
55 57  
56 58  
57 59  
58 60  
59 61  
... ... @@ -204,36 +206,40 @@
204 206 show_stack(current, (unsigned long *) regs->u_regs[UREG_FP]);
205 207 }
206 208  
207   -struct global_reg_snapshot global_reg_snapshot[NR_CPUS];
208   -static DEFINE_SPINLOCK(global_reg_snapshot_lock);
  209 +union global_cpu_snapshot global_cpu_snapshot[NR_CPUS];
  210 +static DEFINE_SPINLOCK(global_cpu_snapshot_lock);
209 211  
210 212 static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs,
211 213 int this_cpu)
212 214 {
  215 + struct global_reg_snapshot *rp;
  216 +
213 217 flushw_all();
214 218  
215   - global_reg_snapshot[this_cpu].tstate = regs->tstate;
216   - global_reg_snapshot[this_cpu].tpc = regs->tpc;
217   - global_reg_snapshot[this_cpu].tnpc = regs->tnpc;
218   - global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7];
  219 + rp = &global_cpu_snapshot[this_cpu].reg;
219 220  
  221 + rp->tstate = regs->tstate;
  222 + rp->tpc = regs->tpc;
  223 + rp->tnpc = regs->tnpc;
  224 + rp->o7 = regs->u_regs[UREG_I7];
  225 +
220 226 if (regs->tstate & TSTATE_PRIV) {
221 227 struct reg_window *rw;
222 228  
223 229 rw = (struct reg_window *)
224 230 (regs->u_regs[UREG_FP] + STACK_BIAS);
225 231 if (kstack_valid(tp, (unsigned long) rw)) {
226   - global_reg_snapshot[this_cpu].i7 = rw->ins[7];
  232 + rp->i7 = rw->ins[7];
227 233 rw = (struct reg_window *)
228 234 (rw->ins[6] + STACK_BIAS);
229 235 if (kstack_valid(tp, (unsigned long) rw))
230   - global_reg_snapshot[this_cpu].rpc = rw->ins[7];
  236 + rp->rpc = rw->ins[7];
231 237 }
232 238 } else {
233   - global_reg_snapshot[this_cpu].i7 = 0;
234   - global_reg_snapshot[this_cpu].rpc = 0;
  239 + rp->i7 = 0;
  240 + rp->rpc = 0;
235 241 }
236   - global_reg_snapshot[this_cpu].thread = tp;
  242 + rp->thread = tp;
237 243 }
238 244  
239 245 /* In order to avoid hangs we do not try to synchronize with the
240 246  
... ... @@ -261,9 +267,9 @@
261 267 if (!regs)
262 268 regs = tp->kregs;
263 269  
264   - spin_lock_irqsave(&global_reg_snapshot_lock, flags);
  270 + spin_lock_irqsave(&global_cpu_snapshot_lock, flags);
265 271  
266   - memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
  272 + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
267 273  
268 274 this_cpu = raw_smp_processor_id();
269 275  
... ... @@ -272,7 +278,7 @@
272 278 smp_fetch_global_regs();
273 279  
274 280 for_each_online_cpu(cpu) {
275   - struct global_reg_snapshot *gp = &global_reg_snapshot[cpu];
  281 + struct global_reg_snapshot *gp = &global_cpu_snapshot[cpu].reg;
276 282  
277 283 __global_reg_poll(gp);
278 284  
279 285  
... ... @@ -295,9 +301,9 @@
295 301 }
296 302 }
297 303  
298   - memset(global_reg_snapshot, 0, sizeof(global_reg_snapshot));
  304 + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
299 305  
300   - spin_unlock_irqrestore(&global_reg_snapshot_lock, flags);
  306 + spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
301 307 }
302 308  
303 309 #ifdef CONFIG_MAGIC_SYSRQ
304 310  
305 311  
306 312  
... ... @@ -309,16 +315,90 @@
309 315  
310 316 static struct sysrq_key_op sparc_globalreg_op = {
311 317 .handler = sysrq_handle_globreg,
312   - .help_msg = "Globalregs",
  318 + .help_msg = "global-regs(Y)",
313 319 .action_msg = "Show Global CPU Regs",
314 320 };
315 321  
316   -static int __init sparc_globreg_init(void)
  322 +static void __global_pmu_self(int this_cpu)
317 323 {
318   - return register_sysrq_key('y', &sparc_globalreg_op);
  324 + struct global_pmu_snapshot *pp;
  325 + int i, num;
  326 +
  327 + pp = &global_cpu_snapshot[this_cpu].pmu;
  328 +
  329 + num = 1;
  330 + if (tlb_type == hypervisor &&
  331 + sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
  332 + num = 4;
  333 +
  334 + for (i = 0; i < num; i++) {
  335 + pp->pcr[i] = pcr_ops->read_pcr(i);
  336 + pp->pic[i] = pcr_ops->read_pic(i);
  337 + }
319 338 }
320 339  
321   -core_initcall(sparc_globreg_init);
  340 +static void __global_pmu_poll(struct global_pmu_snapshot *pp)
  341 +{
  342 + int limit = 0;
  343 +
  344 + while (!pp->pcr[0] && ++limit < 100) {
  345 + barrier();
  346 + udelay(1);
  347 + }
  348 +}
  349 +
  350 +static void pmu_snapshot_all_cpus(void)
  351 +{
  352 + unsigned long flags;
  353 + int this_cpu, cpu;
  354 +
  355 + spin_lock_irqsave(&global_cpu_snapshot_lock, flags);
  356 +
  357 + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
  358 +
  359 + this_cpu = raw_smp_processor_id();
  360 +
  361 + __global_pmu_self(this_cpu);
  362 +
  363 + smp_fetch_global_pmu();
  364 +
  365 + for_each_online_cpu(cpu) {
  366 + struct global_pmu_snapshot *pp = &global_cpu_snapshot[cpu].pmu;
  367 +
  368 + __global_pmu_poll(pp);
  369 +
  370 + printk("%c CPU[%3d]: PCR[%08lx:%08lx:%08lx:%08lx] PIC[%08lx:%08lx:%08lx:%08lx]\n",
  371 + (cpu == this_cpu ? '*' : ' '), cpu,
  372 + pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3],
  373 + pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]);
  374 + }
  375 +
  376 + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
  377 +
  378 + spin_unlock_irqrestore(&global_cpu_snapshot_lock, flags);
  379 +}
  380 +
  381 +static void sysrq_handle_globpmu(int key)
  382 +{
  383 + pmu_snapshot_all_cpus();
  384 +}
  385 +
  386 +static struct sysrq_key_op sparc_globalpmu_op = {
  387 + .handler = sysrq_handle_globpmu,
  388 + .help_msg = "global-pmu(X)",
  389 + .action_msg = "Show Global PMU Regs",
  390 +};
  391 +
  392 +static int __init sparc_sysrq_init(void)
  393 +{
  394 + int ret = register_sysrq_key('y', &sparc_globalreg_op);
  395 +
  396 + if (!ret)
  397 + ret = register_sysrq_key('x', &sparc_globalpmu_op);
  398 + return ret;
  399 +}
  400 +
  401 +core_initcall(sparc_sysrq_init);
322 402  
323 403 #endif
324 404  
arch/sparc/kernel/smp_64.c
... ... @@ -852,6 +852,8 @@
852 852 extern unsigned long xcall_flush_tlb_pending;
853 853 extern unsigned long xcall_flush_tlb_kernel_range;
854 854 extern unsigned long xcall_fetch_glob_regs;
  855 +extern unsigned long xcall_fetch_glob_pmu;
  856 +extern unsigned long xcall_fetch_glob_pmu_n4;
855 857 extern unsigned long xcall_receive_signal;
856 858 extern unsigned long xcall_new_mmu_context_version;
857 859 #ifdef CONFIG_KGDB
... ... @@ -998,6 +1000,15 @@
998 1000 void smp_fetch_global_regs(void)
999 1001 {
1000 1002 smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
  1003 +}
  1004 +
  1005 +void smp_fetch_global_pmu(void)
  1006 +{
  1007 + if (tlb_type == hypervisor &&
  1008 + sun4v_chip_type >= SUN4V_CHIP_NIAGARA4)
  1009 + smp_cross_call(&xcall_fetch_glob_pmu_n4, 0, 0, 0);
  1010 + else
  1011 + smp_cross_call(&xcall_fetch_glob_pmu, 0, 0, 0);
1001 1012 }
1002 1013  
1003 1014 /* We know that the window frames of the user have been flushed
arch/sparc/mm/ultra.S
... ... @@ -481,8 +481,8 @@
481 481  
482 482 .globl xcall_fetch_glob_regs
483 483 xcall_fetch_glob_regs:
484   - sethi %hi(global_reg_snapshot), %g1
485   - or %g1, %lo(global_reg_snapshot), %g1
  484 + sethi %hi(global_cpu_snapshot), %g1
  485 + or %g1, %lo(global_cpu_snapshot), %g1
486 486 __GET_CPUID(%g2)
487 487 sllx %g2, 6, %g3
488 488 add %g1, %g3, %g1
... ... @@ -507,6 +507,66 @@
507 507 add %g7, %g2, %g7
508 508 ldx [%g7 + TRAP_PER_CPU_THREAD], %g3
509 509 stx %g3, [%g1 + GR_SNAP_THREAD]
  510 + retry
  511 +
  512 + .globl xcall_fetch_glob_pmu
  513 +xcall_fetch_glob_pmu:
  514 + sethi %hi(global_cpu_snapshot), %g1
  515 + or %g1, %lo(global_cpu_snapshot), %g1
  516 + __GET_CPUID(%g2)
  517 + sllx %g2, 6, %g3
  518 + add %g1, %g3, %g1
  519 + rd %pic, %g7
  520 + stx %g7, [%g1 + (4 * 8)]
  521 + rd %pcr, %g7
  522 + stx %g7, [%g1 + (0 * 8)]
  523 + retry
  524 +
  525 + .globl xcall_fetch_glob_pmu_n4
  526 +xcall_fetch_glob_pmu_n4:
  527 + sethi %hi(global_cpu_snapshot), %g1
  528 + or %g1, %lo(global_cpu_snapshot), %g1
  529 + __GET_CPUID(%g2)
  530 + sllx %g2, 6, %g3
  531 + add %g1, %g3, %g1
  532 +
  533 + ldxa [%g0] ASI_PIC, %g7
  534 + stx %g7, [%g1 + (4 * 8)]
  535 + mov 0x08, %g3
  536 + ldxa [%g3] ASI_PIC, %g7
  537 + stx %g7, [%g1 + (5 * 8)]
  538 + mov 0x10, %g3
  539 + ldxa [%g3] ASI_PIC, %g7
  540 + stx %g7, [%g1 + (6 * 8)]
  541 + mov 0x18, %g3
  542 + ldxa [%g3] ASI_PIC, %g7
  543 + stx %g7, [%g1 + (7 * 8)]
  544 +
  545 + mov %o0, %g2
  546 + mov %o1, %g3
  547 + mov %o5, %g7
  548 +
  549 + mov HV_FAST_VT_GET_PERFREG, %o5
  550 + mov 3, %o0
  551 + ta HV_FAST_TRAP
  552 + stx %o1, [%g1 + (3 * 8)]
  553 + mov HV_FAST_VT_GET_PERFREG, %o5
  554 + mov 2, %o0
  555 + ta HV_FAST_TRAP
  556 + stx %o1, [%g1 + (2 * 8)]
  557 + mov HV_FAST_VT_GET_PERFREG, %o5
  558 + mov 1, %o0
  559 + ta HV_FAST_TRAP
  560 + stx %o1, [%g1 + (1 * 8)]
  561 + mov HV_FAST_VT_GET_PERFREG, %o5
  562 + mov 0, %o0
  563 + ta HV_FAST_TRAP
  564 + stx %o1, [%g1 + (0 * 8)]
  565 +
  566 + mov %g2, %o0
  567 + mov %g3, %o1
  568 + mov %g7, %o5
  569 +
510 570 retry
511 571  
512 572 #ifdef DCACHE_ALIASING_POSSIBLE
... ... @@ -452,6 +452,7 @@
452 452 NULL, /* v */
453 453 &sysrq_showstate_blocked_op, /* w */
454 454 /* x: May be registered on ppc/powerpc for xmon */
  455 + /* x: May be registered on sparc64 for global PMU dump */
455 456 NULL, /* x */
456 457 /* y: May be registered on sparc64 for global register dump */
457 458 NULL, /* y */