Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : #include <linux/perf_event.h>
3 : : #include <linux/sysfs.h>
4 : : #include <linux/nospec.h>
5 : : #include <asm/intel-family.h>
6 : : #include "probe.h"
7 : :
8 : : enum perf_msr_id {
9 : : PERF_MSR_TSC = 0,
10 : : PERF_MSR_APERF = 1,
11 : : PERF_MSR_MPERF = 2,
12 : : PERF_MSR_PPERF = 3,
13 : : PERF_MSR_SMI = 4,
14 : : PERF_MSR_PTSC = 5,
15 : : PERF_MSR_IRPERF = 6,
16 : : PERF_MSR_THERM = 7,
17 : : PERF_MSR_EVENT_MAX,
18 : : };
19 : :
20 : 56 : static bool test_aperfmperf(int idx, void *data)
21 : : {
22 : 56 : return boot_cpu_has(X86_FEATURE_APERFMPERF);
23 : : }
24 : :
25 : 28 : static bool test_ptsc(int idx, void *data)
26 : : {
27 : 28 : return boot_cpu_has(X86_FEATURE_PTSC);
28 : : }
29 : :
30 : 28 : static bool test_irperf(int idx, void *data)
31 : : {
32 : 28 : return boot_cpu_has(X86_FEATURE_IRPERF);
33 : : }
34 : :
35 : 28 : static bool test_therm_status(int idx, void *data)
36 : : {
37 : 28 : return boot_cpu_has(X86_FEATURE_DTHERM);
38 : : }
39 : :
40 : 56 : static bool test_intel(int idx, void *data)
41 : : {
42 [ - + ]: 56 : if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
43 : : boot_cpu_data.x86 != 6)
44 : : return false;
45 : :
46 [ # # # ]: 0 : switch (boot_cpu_data.x86_model) {
47 : 0 : case INTEL_FAM6_NEHALEM:
48 : : case INTEL_FAM6_NEHALEM_G:
49 : : case INTEL_FAM6_NEHALEM_EP:
50 : : case INTEL_FAM6_NEHALEM_EX:
51 : :
52 : : case INTEL_FAM6_WESTMERE:
53 : : case INTEL_FAM6_WESTMERE_EP:
54 : : case INTEL_FAM6_WESTMERE_EX:
55 : :
56 : : case INTEL_FAM6_SANDYBRIDGE:
57 : : case INTEL_FAM6_SANDYBRIDGE_X:
58 : :
59 : : case INTEL_FAM6_IVYBRIDGE:
60 : : case INTEL_FAM6_IVYBRIDGE_X:
61 : :
62 : : case INTEL_FAM6_HASWELL:
63 : : case INTEL_FAM6_HASWELL_X:
64 : : case INTEL_FAM6_HASWELL_L:
65 : : case INTEL_FAM6_HASWELL_G:
66 : :
67 : : case INTEL_FAM6_BROADWELL:
68 : : case INTEL_FAM6_BROADWELL_D:
69 : : case INTEL_FAM6_BROADWELL_G:
70 : : case INTEL_FAM6_BROADWELL_X:
71 : :
72 : : case INTEL_FAM6_ATOM_SILVERMONT:
73 : : case INTEL_FAM6_ATOM_SILVERMONT_D:
74 : : case INTEL_FAM6_ATOM_AIRMONT:
75 : :
76 : : case INTEL_FAM6_ATOM_GOLDMONT:
77 : : case INTEL_FAM6_ATOM_GOLDMONT_D:
78 : : case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
79 : : case INTEL_FAM6_ATOM_TREMONT_D:
80 : : case INTEL_FAM6_ATOM_TREMONT:
81 : :
82 : : case INTEL_FAM6_XEON_PHI_KNL:
83 : : case INTEL_FAM6_XEON_PHI_KNM:
84 [ # # ]: 0 : if (idx == PERF_MSR_SMI)
85 : 0 : return true;
86 : : break;
87 : :
88 : 0 : case INTEL_FAM6_SKYLAKE_L:
89 : : case INTEL_FAM6_SKYLAKE:
90 : : case INTEL_FAM6_SKYLAKE_X:
91 : : case INTEL_FAM6_KABYLAKE_L:
92 : : case INTEL_FAM6_KABYLAKE:
93 : : case INTEL_FAM6_COMETLAKE_L:
94 : : case INTEL_FAM6_COMETLAKE:
95 : : case INTEL_FAM6_ICELAKE_L:
96 : : case INTEL_FAM6_ICELAKE:
97 : : case INTEL_FAM6_ICELAKE_X:
98 : : case INTEL_FAM6_ICELAKE_D:
99 : : case INTEL_FAM6_TIGERLAKE_L:
100 : : case INTEL_FAM6_TIGERLAKE:
101 [ # # ]: 0 : if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
102 : 0 : return true;
103 : : break;
104 : : }
105 : :
106 : : return false;
107 : : }
108 : :
109 : : PMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" );
110 : : PMU_EVENT_ATTR_STRING(aperf, attr_aperf, "event=0x01" );
111 : : PMU_EVENT_ATTR_STRING(mperf, attr_mperf, "event=0x02" );
112 : : PMU_EVENT_ATTR_STRING(pperf, attr_pperf, "event=0x03" );
113 : : PMU_EVENT_ATTR_STRING(smi, attr_smi, "event=0x04" );
114 : : PMU_EVENT_ATTR_STRING(ptsc, attr_ptsc, "event=0x05" );
115 : : PMU_EVENT_ATTR_STRING(irperf, attr_irperf, "event=0x06" );
116 : : PMU_EVENT_ATTR_STRING(cpu_thermal_margin, attr_therm, "event=0x07" );
117 : : PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot, attr_therm_snap, "1" );
118 : : PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit, attr_therm_unit, "C" );
119 : :
120 : : static unsigned long msr_mask;
121 : :
122 : : PMU_EVENT_GROUP(events, aperf);
123 : : PMU_EVENT_GROUP(events, mperf);
124 : : PMU_EVENT_GROUP(events, pperf);
125 : : PMU_EVENT_GROUP(events, smi);
126 : : PMU_EVENT_GROUP(events, ptsc);
127 : : PMU_EVENT_GROUP(events, irperf);
128 : :
129 : : static struct attribute *attrs_therm[] = {
130 : : &attr_therm.attr.attr,
131 : : &attr_therm_snap.attr.attr,
132 : : &attr_therm_unit.attr.attr,
133 : : NULL,
134 : : };
135 : :
136 : : static struct attribute_group group_therm = {
137 : : .name = "events",
138 : : .attrs = attrs_therm,
139 : : };
140 : :
141 : : static struct perf_msr msr[] = {
142 : : [PERF_MSR_TSC] = { .no_check = true, },
143 : : [PERF_MSR_APERF] = { MSR_IA32_APERF, &group_aperf, test_aperfmperf, },
144 : : [PERF_MSR_MPERF] = { MSR_IA32_MPERF, &group_mperf, test_aperfmperf, },
145 : : [PERF_MSR_PPERF] = { MSR_PPERF, &group_pperf, test_intel, },
146 : : [PERF_MSR_SMI] = { MSR_SMI_COUNT, &group_smi, test_intel, },
147 : : [PERF_MSR_PTSC] = { MSR_F15H_PTSC, &group_ptsc, test_ptsc, },
148 : : [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF, &group_irperf, test_irperf, },
149 : : [PERF_MSR_THERM] = { MSR_IA32_THERM_STATUS, &group_therm, test_therm_status, },
150 : : };
151 : :
152 : : static struct attribute *events_attrs[] = {
153 : : &attr_tsc.attr.attr,
154 : : NULL,
155 : : };
156 : :
157 : : static struct attribute_group events_attr_group = {
158 : : .name = "events",
159 : : .attrs = events_attrs,
160 : : };
161 : :
162 : 0 : PMU_FORMAT_ATTR(event, "config:0-63");
163 : : static struct attribute *format_attrs[] = {
164 : : &format_attr_event.attr,
165 : : NULL,
166 : : };
167 : : static struct attribute_group format_attr_group = {
168 : : .name = "format",
169 : : .attrs = format_attrs,
170 : : };
171 : :
172 : : static const struct attribute_group *attr_groups[] = {
173 : : &events_attr_group,
174 : : &format_attr_group,
175 : : NULL,
176 : : };
177 : :
178 : : static const struct attribute_group *attr_update[] = {
179 : : &group_aperf,
180 : : &group_mperf,
181 : : &group_pperf,
182 : : &group_smi,
183 : : &group_ptsc,
184 : : &group_irperf,
185 : : &group_therm,
186 : : NULL,
187 : : };
188 : :
189 : 0 : static int msr_event_init(struct perf_event *event)
190 : : {
191 : 0 : u64 cfg = event->attr.config;
192 : :
193 [ # # ]: 0 : if (event->attr.type != event->pmu->type)
194 : : return -ENOENT;
195 : :
196 : : /* unsupported modes and filters */
197 [ # # ]: 0 : if (event->attr.sample_period) /* no sampling */
198 : : return -EINVAL;
199 : :
200 [ # # ]: 0 : if (cfg >= PERF_MSR_EVENT_MAX)
201 : : return -EINVAL;
202 : :
203 : 0 : cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX);
204 : :
205 [ # # ]: 0 : if (!(msr_mask & (1 << cfg)))
206 : : return -EINVAL;
207 : :
208 : 0 : event->hw.idx = -1;
209 : 0 : event->hw.event_base = msr[cfg].msr;
210 : 0 : event->hw.config = cfg;
211 : :
212 : 0 : return 0;
213 : : }
214 : :
215 : : static inline u64 msr_read_counter(struct perf_event *event)
216 : : {
217 : : u64 now;
218 : :
219 : : if (event->hw.event_base)
220 : : rdmsrl(event->hw.event_base, now);
221 : : else
222 : : now = rdtsc_ordered();
223 : :
224 : : return now;
225 : : }
226 : :
227 : 0 : static void msr_event_update(struct perf_event *event)
228 : : {
229 : 0 : u64 prev, now;
230 : 0 : s64 delta;
231 : :
232 : : /* Careful, an NMI might modify the previous event value: */
233 : 0 : again:
234 : 0 : prev = local64_read(&event->hw.prev_count);
235 : 0 : now = msr_read_counter(event);
236 : :
237 [ # # ]: 0 : if (local64_cmpxchg(&event->hw.prev_count, prev, now) != prev)
238 : 0 : goto again;
239 : :
240 : 0 : delta = now - prev;
241 [ # # ]: 0 : if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) {
242 : 0 : delta = sign_extend64(delta, 31);
243 : 0 : local64_add(delta, &event->count);
244 [ # # ]: 0 : } else if (unlikely(event->hw.event_base == MSR_IA32_THERM_STATUS)) {
245 : : /* If valid, extract digital readout, otherwise set to -1: */
246 [ # # ]: 0 : now = now & (1ULL << 31) ? (now >> 16) & 0x3f : -1;
247 : 0 : local64_set(&event->count, now);
248 : : } else {
249 : 0 : local64_add(delta, &event->count);
250 : : }
251 : 0 : }
252 : :
253 : 0 : static void msr_event_start(struct perf_event *event, int flags)
254 : : {
255 : 0 : u64 now = msr_read_counter(event);
256 : :
257 : 0 : local64_set(&event->hw.prev_count, now);
258 : 0 : }
259 : :
260 : 0 : static void msr_event_stop(struct perf_event *event, int flags)
261 : : {
262 : 0 : msr_event_update(event);
263 : 0 : }
264 : :
265 : 0 : static void msr_event_del(struct perf_event *event, int flags)
266 : : {
267 : 0 : msr_event_stop(event, PERF_EF_UPDATE);
268 : 0 : }
269 : :
270 : 0 : static int msr_event_add(struct perf_event *event, int flags)
271 : : {
272 [ # # ]: 0 : if (flags & PERF_EF_START)
273 : 0 : msr_event_start(event, flags);
274 : :
275 : 0 : return 0;
276 : : }
277 : :
278 : : static struct pmu pmu_msr = {
279 : : .task_ctx_nr = perf_sw_context,
280 : : .attr_groups = attr_groups,
281 : : .event_init = msr_event_init,
282 : : .add = msr_event_add,
283 : : .del = msr_event_del,
284 : : .start = msr_event_start,
285 : : .stop = msr_event_stop,
286 : : .read = msr_event_update,
287 : : .capabilities = PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE,
288 : : .attr_update = attr_update,
289 : : };
290 : :
291 : 28 : static int __init msr_init(void)
292 : : {
293 [ - + ]: 28 : if (!boot_cpu_has(X86_FEATURE_TSC)) {
294 : 0 : pr_cont("no MSR PMU driver.\n");
295 : 0 : return 0;
296 : : }
297 : :
298 : 28 : msr_mask = perf_msr_probe(msr, PERF_MSR_EVENT_MAX, true, NULL);
299 : :
300 : 28 : perf_pmu_register(&pmu_msr, "msr", -1);
301 : :
302 : 28 : return 0;
303 : : }
304 : : device_initcall(msr_init);
|