]>
Commit | Line | Data |
---|---|---|
a17ae4c3 | 1 | // SPDX-License-Identifier: GPL-2.0 |
212188a5 HB |
2 | /* |
3 | * Performance event support for s390x | |
4 | * | |
443e802b | 5 | * Copyright IBM Corp. 2012, 2013 |
212188a5 | 6 | * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> |
212188a5 HB |
7 | */ |
8 | #define KMSG_COMPONENT "perf" | |
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/perf_event.h> | |
b764bb1c | 13 | #include <linux/kvm_host.h> |
212188a5 HB |
14 | #include <linux/percpu.h> |
15 | #include <linux/export.h> | |
99c64b66 | 16 | #include <linux/seq_file.h> |
8c069ff4 | 17 | #include <linux/spinlock.h> |
c7168325 | 18 | #include <linux/sysfs.h> |
212188a5 HB |
19 | #include <asm/irq.h> |
20 | #include <asm/cpu_mf.h> | |
21 | #include <asm/lowcore.h> | |
22 | #include <asm/processor.h> | |
99c64b66 | 23 | #include <asm/sysinfo.h> |
212188a5 HB |
24 | |
25 | const char *perf_pmu_name(void) | |
26 | { | |
27 | if (cpum_cf_avail() || cpum_sf_avail()) | |
99c64b66 | 28 | return "CPU-Measurement Facilities (CPU-MF)"; |
212188a5 HB |
29 | return "pmu"; |
30 | } | |
31 | EXPORT_SYMBOL(perf_pmu_name); | |
32 | ||
33 | int perf_num_counters(void) | |
34 | { | |
35 | int num = 0; | |
36 | ||
37 | if (cpum_cf_avail()) | |
38 | num += PERF_CPUM_CF_MAX_CTR; | |
8c069ff4 HB |
39 | if (cpum_sf_avail()) |
40 | num += PERF_CPUM_SF_MAX_CTR; | |
212188a5 HB |
41 | |
42 | return num; | |
43 | } | |
44 | EXPORT_SYMBOL(perf_num_counters); | |
45 | ||
b764bb1c HG |
46 | static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs) |
47 | { | |
48 | struct stack_frame *stack = (struct stack_frame *) regs->gprs[15]; | |
49 | ||
50 | if (!stack) | |
51 | return NULL; | |
52 | ||
53 | return (struct kvm_s390_sie_block *) stack->empty1[0]; | |
54 | } | |
55 | ||
56 | static bool is_in_guest(struct pt_regs *regs) | |
57 | { | |
b764bb1c HG |
58 | if (user_mode(regs)) |
59 | return false; | |
61aa4884 | 60 | #if IS_ENABLED(CONFIG_KVM) |
198b1bf8 HC |
61 | return instruction_pointer(regs) == (unsigned long) &sie_exit; |
62 | #else | |
63 | return false; | |
64 | #endif | |
b764bb1c HG |
65 | } |
66 | ||
67 | static unsigned long guest_is_user_mode(struct pt_regs *regs) | |
68 | { | |
69 | return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE; | |
70 | } | |
71 | ||
72 | static unsigned long instruction_pointer_guest(struct pt_regs *regs) | |
73 | { | |
9cb1ccec | 74 | return sie_block(regs)->gpsw.addr; |
b764bb1c HG |
75 | } |
76 | ||
77 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | |
78 | { | |
79 | return is_in_guest(regs) ? instruction_pointer_guest(regs) | |
80 | : instruction_pointer(regs); | |
81 | } | |
82 | ||
83 | static unsigned long perf_misc_guest_flags(struct pt_regs *regs) | |
84 | { | |
85 | return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER | |
86 | : PERF_RECORD_MISC_GUEST_KERNEL; | |
87 | } | |
88 | ||
443e802b HB |
89 | static unsigned long perf_misc_flags_sf(struct pt_regs *regs) |
90 | { | |
91 | struct perf_sf_sde_regs *sde_regs; | |
92 | unsigned long flags; | |
93 | ||
94 | sde_regs = (struct perf_sf_sde_regs *) ®s->int_parm_long; | |
95 | if (sde_regs->in_guest) | |
96 | flags = user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER | |
97 | : PERF_RECORD_MISC_GUEST_KERNEL; | |
98 | else | |
99 | flags = user_mode(regs) ? PERF_RECORD_MISC_USER | |
100 | : PERF_RECORD_MISC_KERNEL; | |
101 | return flags; | |
102 | } | |
103 | ||
b764bb1c HG |
104 | unsigned long perf_misc_flags(struct pt_regs *regs) |
105 | { | |
443e802b HB |
106 | /* Check if the cpum_sf PMU has created the pt_regs structure. |
107 | * In this case, perf misc flags can be easily extracted. Otherwise, | |
108 | * do regular checks on the pt_regs content. | |
109 | */ | |
110 | if (regs->int_code == 0x1407 && regs->int_parm == CPU_MF_INT_SF_PRA) | |
111 | if (!regs->gprs[15]) | |
112 | return perf_misc_flags_sf(regs); | |
113 | ||
b764bb1c HG |
114 | if (is_in_guest(regs)) |
115 | return perf_misc_guest_flags(regs); | |
116 | ||
117 | return user_mode(regs) ? PERF_RECORD_MISC_USER | |
118 | : PERF_RECORD_MISC_KERNEL; | |
119 | } | |
120 | ||
443fc8a3 | 121 | static void print_debug_cf(void) |
212188a5 HB |
122 | { |
123 | struct cpumf_ctr_info cf_info; | |
8c069ff4 | 124 | int cpu = smp_processor_id(); |
212188a5 | 125 | |
212188a5 | 126 | memset(&cf_info, 0, sizeof(cf_info)); |
ae6834c1 | 127 | if (!qctri(&cf_info)) |
212188a5 HB |
128 | pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n", |
129 | cpu, cf_info.cfvn, cf_info.csvn, | |
130 | cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl); | |
8c069ff4 | 131 | } |
212188a5 | 132 | |
8c069ff4 HB |
133 | static void print_debug_sf(void) |
134 | { | |
135 | struct hws_qsi_info_block si; | |
136 | int cpu = smp_processor_id(); | |
137 | ||
138 | memset(&si, 0, sizeof(si)); | |
7e75fc3f | 139 | if (qsi(&si)) |
8c069ff4 | 140 | return; |
8c069ff4 | 141 | |
f85168e4 | 142 | pr_info("CPU[%i] CPUM_SF: basic=%i diag=%i min=%lu max=%lu cpu_speed=%u\n", |
7e75fc3f HB |
143 | cpu, si.as, si.ad, si.min_sampl_rate, si.max_sampl_rate, |
144 | si.cpu_speed); | |
145 | ||
146 | if (si.as) | |
147 | pr_info("CPU[%i] CPUM_SF: Basic-sampling: a=%i e=%i c=%i" | |
f85168e4 | 148 | " bsdes=%i tear=%016lx dear=%016lx\n", cpu, |
7e75fc3f HB |
149 | si.as, si.es, si.cs, si.bsdes, si.tear, si.dear); |
150 | if (si.ad) | |
151 | pr_info("CPU[%i] CPUM_SF: Diagnostic-sampling: a=%i e=%i c=%i" | |
f85168e4 | 152 | " dsdes=%i tear=%016lx dear=%016lx\n", cpu, |
7e75fc3f | 153 | si.ad, si.ed, si.cd, si.dsdes, si.tear, si.dear); |
8c069ff4 HB |
154 | } |
155 | ||
156 | void perf_event_print_debug(void) | |
157 | { | |
158 | unsigned long flags; | |
159 | ||
160 | local_irq_save(flags); | |
161 | if (cpum_cf_avail()) | |
162 | print_debug_cf(); | |
163 | if (cpum_sf_avail()) | |
164 | print_debug_sf(); | |
212188a5 HB |
165 | local_irq_restore(flags); |
166 | } | |
167 | ||
99c64b66 HB |
168 | /* Service level infrastructure */ |
169 | static void sl_print_counter(struct seq_file *m) | |
170 | { | |
171 | struct cpumf_ctr_info ci; | |
172 | ||
173 | memset(&ci, 0, sizeof(ci)); | |
174 | if (qctri(&ci)) | |
175 | return; | |
176 | ||
177 | seq_printf(m, "CPU-MF: Counter facility: version=%u.%u " | |
178 | "authorization=%04x\n", ci.cfvn, ci.csvn, ci.auth_ctl); | |
179 | } | |
180 | ||
181 | static void sl_print_sampling(struct seq_file *m) | |
182 | { | |
183 | struct hws_qsi_info_block si; | |
184 | ||
185 | memset(&si, 0, sizeof(si)); | |
186 | if (qsi(&si)) | |
187 | return; | |
188 | ||
189 | if (!si.as && !si.ad) | |
190 | return; | |
191 | ||
192 | seq_printf(m, "CPU-MF: Sampling facility: min_rate=%lu max_rate=%lu" | |
193 | " cpu_speed=%u\n", si.min_sampl_rate, si.max_sampl_rate, | |
194 | si.cpu_speed); | |
195 | if (si.as) | |
196 | seq_printf(m, "CPU-MF: Sampling facility: mode=basic" | |
197 | " sample_size=%u\n", si.bsdes); | |
198 | if (si.ad) | |
199 | seq_printf(m, "CPU-MF: Sampling facility: mode=diagnostic" | |
200 | " sample_size=%u\n", si.dsdes); | |
201 | } | |
202 | ||
203 | static void service_level_perf_print(struct seq_file *m, | |
204 | struct service_level *sl) | |
205 | { | |
206 | if (cpum_cf_avail()) | |
207 | sl_print_counter(m); | |
208 | if (cpum_sf_avail()) | |
209 | sl_print_sampling(m); | |
210 | } | |
211 | ||
212 | static struct service_level service_level_perf = { | |
213 | .seq_print = service_level_perf_print, | |
214 | }; | |
215 | ||
216 | static int __init service_level_perf_register(void) | |
217 | { | |
218 | return register_service_level(&service_level_perf); | |
219 | } | |
220 | arch_initcall(service_level_perf_register); | |
221 | ||
d0208639 | 222 | static int __perf_callchain_kernel(void *data, unsigned long address, int reliable) |
212188a5 | 223 | { |
cfbcf468 | 224 | struct perf_callchain_entry_ctx *entry = data; |
758d39eb HC |
225 | |
226 | perf_callchain_store(entry, address); | |
227 | return 0; | |
212188a5 HB |
228 | } |
229 | ||
cfbcf468 | 230 | void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, |
212188a5 HB |
231 | struct pt_regs *regs) |
232 | { | |
212188a5 HB |
233 | if (user_mode(regs)) |
234 | return; | |
758d39eb | 235 | dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]); |
212188a5 | 236 | } |
c7168325 | 237 | |
7eb792bf | 238 | /* Perf definitions for PMU event attributes in sysfs */ |
c7168325 HB |
239 | ssize_t cpumf_events_sysfs_show(struct device *dev, |
240 | struct device_attribute *attr, char *page) | |
241 | { | |
242 | struct perf_pmu_events_attr *pmu_attr; | |
243 | ||
244 | pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); | |
c39457ff | 245 | return sprintf(page, "event=0x%04llx\n", pmu_attr->id); |
c7168325 | 246 | } |