aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/perf_event.c
diff options
context:
space:
mode:
authorHendrik Brueckner <brueckner@linux.vnet.ibm.com>2012-03-23 06:13:06 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-03-23 06:13:25 -0400
commit212188a596d17d519842ef2173150315735b54e1 (patch)
tree22f4f391fc9979a231028e45fd11b8a6a6c99232 /arch/s390/kernel/perf_event.c
parentb03d541aa45b52e1b723890121a9fe3920eb438b (diff)
[S390] perf: add support for s390x CPU counters
Add a perf PMU to access the CPU-measurement counter facility CPUM CF. CPUM CF provides multiple counter sets for measuring generic, problem-state, and crypto activaties. Also an extended counter set for the IBM System z10 and IBM z196 mainframes is available. Counters from the basic and problem-state counter set are mapped to generic perf hardware events. Other counters are accessible through raw events. For a list of available counter sets and counters, see: - The Load-Program-Parameter and the CPU-Measurement Facilities (SA23-2260) - The CPU-Measurement Facility Extended Counters Definition for z10 and z196 (SA23-2261) Reviewed-by: Jan Glauber <jang@linux.vnet.ibm.com> Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/perf_event.c')
-rw-r--r--arch/s390/kernel/perf_event.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c
new file mode 100644
index 000000000000..609f985198cf
--- /dev/null
+++ b/arch/s390/kernel/perf_event.c
@@ -0,0 +1,125 @@
1/*
2 * Performance event support for s390x
3 *
4 * Copyright IBM Corp. 2012
5 * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License (version 2 only)
9 * as published by the Free Software Foundation.
10 */
11#define KMSG_COMPONENT "perf"
12#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
13
14#include <linux/kernel.h>
15#include <linux/perf_event.h>
16#include <linux/percpu.h>
17#include <linux/export.h>
18#include <asm/system.h>
19#include <asm/irq.h>
20#include <asm/cpu_mf.h>
21#include <asm/lowcore.h>
22#include <asm/processor.h>
23
24const char *perf_pmu_name(void)
25{
26 if (cpum_cf_avail() || cpum_sf_avail())
27 return "CPU-measurement facilities (CPUMF)";
28 return "pmu";
29}
30EXPORT_SYMBOL(perf_pmu_name);
31
32int perf_num_counters(void)
33{
34 int num = 0;
35
36 if (cpum_cf_avail())
37 num += PERF_CPUM_CF_MAX_CTR;
38
39 return num;
40}
41EXPORT_SYMBOL(perf_num_counters);
42
43void perf_event_print_debug(void)
44{
45 struct cpumf_ctr_info cf_info;
46 unsigned long flags;
47 int cpu;
48
49 if (!cpum_cf_avail())
50 return;
51
52 local_irq_save(flags);
53
54 cpu = smp_processor_id();
55 memset(&cf_info, 0, sizeof(cf_info));
56 if (!qctri(&cf_info)) {
57 pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n",
58 cpu, cf_info.cfvn, cf_info.csvn,
59 cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl);
60 print_hex_dump_bytes("CPUMF Query: ", DUMP_PREFIX_OFFSET,
61 &cf_info, sizeof(cf_info));
62 }
63
64 local_irq_restore(flags);
65}
66
67/* See also arch/s390/kernel/traps.c */
68static unsigned long __store_trace(struct perf_callchain_entry *entry,
69 unsigned long sp,
70 unsigned long low, unsigned long high)
71{
72 struct stack_frame *sf;
73 struct pt_regs *regs;
74
75 while (1) {
76 sp = sp & PSW_ADDR_INSN;
77 if (sp < low || sp > high - sizeof(*sf))
78 return sp;
79 sf = (struct stack_frame *) sp;
80 perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
81 /* Follow the backchain. */
82 while (1) {
83 low = sp;
84 sp = sf->back_chain & PSW_ADDR_INSN;
85 if (!sp)
86 break;
87 if (sp <= low || sp > high - sizeof(*sf))
88 return sp;
89 sf = (struct stack_frame *) sp;
90 perf_callchain_store(entry,
91 sf->gprs[8] & PSW_ADDR_INSN);
92 }
93 /* Zero backchain detected, check for interrupt frame. */
94 sp = (unsigned long) (sf + 1);
95 if (sp <= low || sp > high - sizeof(*regs))
96 return sp;
97 regs = (struct pt_regs *) sp;
98 perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
99 low = sp;
100 sp = regs->gprs[15];
101 }
102}
103
104void perf_callchain_kernel(struct perf_callchain_entry *entry,
105 struct pt_regs *regs)
106{
107 unsigned long head;
108 struct stack_frame *head_sf;
109
110 if (user_mode(regs))
111 return;
112
113 head = regs->gprs[15];
114 head_sf = (struct stack_frame *) head;
115
116 if (!head_sf || !head_sf->back_chain)
117 return;
118
119 head = head_sf->back_chain;
120 head = __store_trace(entry, head, S390_lowcore.async_stack - ASYNC_SIZE,
121 S390_lowcore.async_stack);
122
123 __store_trace(entry, head, S390_lowcore.thread_info,
124 S390_lowcore.thread_info + THREAD_SIZE);
125}