.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | #include <linux/perf_event.h> |
---|
| 3 | +#include <linux/sysfs.h> |
---|
3 | 4 | #include <linux/nospec.h> |
---|
4 | 5 | #include <asm/intel-family.h> |
---|
| 6 | +#include "probe.h" |
---|
5 | 7 | |
---|
6 | 8 | enum perf_msr_id { |
---|
7 | 9 | PERF_MSR_TSC = 0, |
---|
.. | .. |
---|
12 | 14 | PERF_MSR_PTSC = 5, |
---|
13 | 15 | PERF_MSR_IRPERF = 6, |
---|
14 | 16 | PERF_MSR_THERM = 7, |
---|
15 | | - PERF_MSR_THERM_SNAP = 8, |
---|
16 | | - PERF_MSR_THERM_UNIT = 9, |
---|
17 | 17 | PERF_MSR_EVENT_MAX, |
---|
18 | 18 | }; |
---|
19 | 19 | |
---|
20 | | -static bool test_aperfmperf(int idx) |
---|
| 20 | +static bool test_aperfmperf(int idx, void *data) |
---|
21 | 21 | { |
---|
22 | 22 | return boot_cpu_has(X86_FEATURE_APERFMPERF); |
---|
23 | 23 | } |
---|
24 | 24 | |
---|
25 | | -static bool test_ptsc(int idx) |
---|
| 25 | +static bool test_ptsc(int idx, void *data) |
---|
26 | 26 | { |
---|
27 | 27 | return boot_cpu_has(X86_FEATURE_PTSC); |
---|
28 | 28 | } |
---|
29 | 29 | |
---|
30 | | -static bool test_irperf(int idx) |
---|
| 30 | +static bool test_irperf(int idx, void *data) |
---|
31 | 31 | { |
---|
32 | 32 | return boot_cpu_has(X86_FEATURE_IRPERF); |
---|
33 | 33 | } |
---|
34 | 34 | |
---|
35 | | -static bool test_therm_status(int idx) |
---|
| 35 | +static bool test_therm_status(int idx, void *data) |
---|
36 | 36 | { |
---|
37 | 37 | return boot_cpu_has(X86_FEATURE_DTHERM); |
---|
38 | 38 | } |
---|
39 | 39 | |
---|
40 | | -static bool test_intel(int idx) |
---|
| 40 | +static bool test_intel(int idx, void *data) |
---|
41 | 41 | { |
---|
42 | 42 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || |
---|
43 | 43 | boot_cpu_data.x86 != 6) |
---|
.. | .. |
---|
59 | 59 | case INTEL_FAM6_IVYBRIDGE: |
---|
60 | 60 | case INTEL_FAM6_IVYBRIDGE_X: |
---|
61 | 61 | |
---|
62 | | - case INTEL_FAM6_HASWELL_CORE: |
---|
| 62 | + case INTEL_FAM6_HASWELL: |
---|
63 | 63 | case INTEL_FAM6_HASWELL_X: |
---|
64 | | - case INTEL_FAM6_HASWELL_ULT: |
---|
65 | | - case INTEL_FAM6_HASWELL_GT3E: |
---|
| 64 | + case INTEL_FAM6_HASWELL_L: |
---|
| 65 | + case INTEL_FAM6_HASWELL_G: |
---|
66 | 66 | |
---|
67 | | - case INTEL_FAM6_BROADWELL_CORE: |
---|
68 | | - case INTEL_FAM6_BROADWELL_XEON_D: |
---|
69 | | - case INTEL_FAM6_BROADWELL_GT3E: |
---|
| 67 | + case INTEL_FAM6_BROADWELL: |
---|
| 68 | + case INTEL_FAM6_BROADWELL_D: |
---|
| 69 | + case INTEL_FAM6_BROADWELL_G: |
---|
70 | 70 | case INTEL_FAM6_BROADWELL_X: |
---|
| 71 | + case INTEL_FAM6_SAPPHIRERAPIDS_X: |
---|
71 | 72 | |
---|
72 | 73 | case INTEL_FAM6_ATOM_SILVERMONT: |
---|
73 | | - case INTEL_FAM6_ATOM_SILVERMONT_X: |
---|
| 74 | + case INTEL_FAM6_ATOM_SILVERMONT_D: |
---|
74 | 75 | case INTEL_FAM6_ATOM_AIRMONT: |
---|
75 | 76 | |
---|
76 | 77 | case INTEL_FAM6_ATOM_GOLDMONT: |
---|
77 | | - case INTEL_FAM6_ATOM_GOLDMONT_X: |
---|
78 | | - |
---|
| 78 | + case INTEL_FAM6_ATOM_GOLDMONT_D: |
---|
79 | 79 | case INTEL_FAM6_ATOM_GOLDMONT_PLUS: |
---|
| 80 | + case INTEL_FAM6_ATOM_TREMONT_D: |
---|
| 81 | + case INTEL_FAM6_ATOM_TREMONT: |
---|
| 82 | + case INTEL_FAM6_ATOM_TREMONT_L: |
---|
80 | 83 | |
---|
81 | 84 | case INTEL_FAM6_XEON_PHI_KNL: |
---|
82 | 85 | case INTEL_FAM6_XEON_PHI_KNM: |
---|
.. | .. |
---|
84 | 87 | return true; |
---|
85 | 88 | break; |
---|
86 | 89 | |
---|
87 | | - case INTEL_FAM6_SKYLAKE_MOBILE: |
---|
88 | | - case INTEL_FAM6_SKYLAKE_DESKTOP: |
---|
| 90 | + case INTEL_FAM6_SKYLAKE_L: |
---|
| 91 | + case INTEL_FAM6_SKYLAKE: |
---|
89 | 92 | case INTEL_FAM6_SKYLAKE_X: |
---|
90 | | - case INTEL_FAM6_KABYLAKE_MOBILE: |
---|
91 | | - case INTEL_FAM6_KABYLAKE_DESKTOP: |
---|
92 | | - case INTEL_FAM6_ICELAKE_MOBILE: |
---|
| 93 | + case INTEL_FAM6_KABYLAKE_L: |
---|
| 94 | + case INTEL_FAM6_KABYLAKE: |
---|
| 95 | + case INTEL_FAM6_COMETLAKE_L: |
---|
| 96 | + case INTEL_FAM6_COMETLAKE: |
---|
| 97 | + case INTEL_FAM6_ICELAKE_L: |
---|
| 98 | + case INTEL_FAM6_ICELAKE: |
---|
| 99 | + case INTEL_FAM6_ICELAKE_X: |
---|
| 100 | + case INTEL_FAM6_ICELAKE_D: |
---|
| 101 | + case INTEL_FAM6_TIGERLAKE_L: |
---|
| 102 | + case INTEL_FAM6_TIGERLAKE: |
---|
93 | 103 | if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF) |
---|
94 | 104 | return true; |
---|
95 | 105 | break; |
---|
.. | .. |
---|
98 | 108 | return false; |
---|
99 | 109 | } |
---|
100 | 110 | |
---|
101 | | -struct perf_msr { |
---|
102 | | - u64 msr; |
---|
103 | | - struct perf_pmu_events_attr *attr; |
---|
104 | | - bool (*test)(int idx); |
---|
| 111 | +PMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" ); |
---|
| 112 | +PMU_EVENT_ATTR_STRING(aperf, attr_aperf, "event=0x01" ); |
---|
| 113 | +PMU_EVENT_ATTR_STRING(mperf, attr_mperf, "event=0x02" ); |
---|
| 114 | +PMU_EVENT_ATTR_STRING(pperf, attr_pperf, "event=0x03" ); |
---|
| 115 | +PMU_EVENT_ATTR_STRING(smi, attr_smi, "event=0x04" ); |
---|
| 116 | +PMU_EVENT_ATTR_STRING(ptsc, attr_ptsc, "event=0x05" ); |
---|
| 117 | +PMU_EVENT_ATTR_STRING(irperf, attr_irperf, "event=0x06" ); |
---|
| 118 | +PMU_EVENT_ATTR_STRING(cpu_thermal_margin, attr_therm, "event=0x07" ); |
---|
| 119 | +PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot, attr_therm_snap, "1" ); |
---|
| 120 | +PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit, attr_therm_unit, "C" ); |
---|
| 121 | + |
---|
| 122 | +static unsigned long msr_mask; |
---|
| 123 | + |
---|
| 124 | +PMU_EVENT_GROUP(events, aperf); |
---|
| 125 | +PMU_EVENT_GROUP(events, mperf); |
---|
| 126 | +PMU_EVENT_GROUP(events, pperf); |
---|
| 127 | +PMU_EVENT_GROUP(events, smi); |
---|
| 128 | +PMU_EVENT_GROUP(events, ptsc); |
---|
| 129 | +PMU_EVENT_GROUP(events, irperf); |
---|
| 130 | + |
---|
| 131 | +static struct attribute *attrs_therm[] = { |
---|
| 132 | + &attr_therm.attr.attr, |
---|
| 133 | + &attr_therm_snap.attr.attr, |
---|
| 134 | + &attr_therm_unit.attr.attr, |
---|
| 135 | + NULL, |
---|
105 | 136 | }; |
---|
106 | 137 | |
---|
107 | | -PMU_EVENT_ATTR_STRING(tsc, evattr_tsc, "event=0x00" ); |
---|
108 | | -PMU_EVENT_ATTR_STRING(aperf, evattr_aperf, "event=0x01" ); |
---|
109 | | -PMU_EVENT_ATTR_STRING(mperf, evattr_mperf, "event=0x02" ); |
---|
110 | | -PMU_EVENT_ATTR_STRING(pperf, evattr_pperf, "event=0x03" ); |
---|
111 | | -PMU_EVENT_ATTR_STRING(smi, evattr_smi, "event=0x04" ); |
---|
112 | | -PMU_EVENT_ATTR_STRING(ptsc, evattr_ptsc, "event=0x05" ); |
---|
113 | | -PMU_EVENT_ATTR_STRING(irperf, evattr_irperf, "event=0x06" ); |
---|
114 | | -PMU_EVENT_ATTR_STRING(cpu_thermal_margin, evattr_therm, "event=0x07" ); |
---|
115 | | -PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot, evattr_therm_snap, "1" ); |
---|
116 | | -PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit, evattr_therm_unit, "C" ); |
---|
| 138 | +static struct attribute_group group_therm = { |
---|
| 139 | + .name = "events", |
---|
| 140 | + .attrs = attrs_therm, |
---|
| 141 | +}; |
---|
117 | 142 | |
---|
118 | 143 | static struct perf_msr msr[] = { |
---|
119 | | - [PERF_MSR_TSC] = { 0, &evattr_tsc, NULL, }, |
---|
120 | | - [PERF_MSR_APERF] = { MSR_IA32_APERF, &evattr_aperf, test_aperfmperf, }, |
---|
121 | | - [PERF_MSR_MPERF] = { MSR_IA32_MPERF, &evattr_mperf, test_aperfmperf, }, |
---|
122 | | - [PERF_MSR_PPERF] = { MSR_PPERF, &evattr_pperf, test_intel, }, |
---|
123 | | - [PERF_MSR_SMI] = { MSR_SMI_COUNT, &evattr_smi, test_intel, }, |
---|
124 | | - [PERF_MSR_PTSC] = { MSR_F15H_PTSC, &evattr_ptsc, test_ptsc, }, |
---|
125 | | - [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF, &evattr_irperf, test_irperf, }, |
---|
126 | | - [PERF_MSR_THERM] = { MSR_IA32_THERM_STATUS, &evattr_therm, test_therm_status, }, |
---|
127 | | - [PERF_MSR_THERM_SNAP] = { MSR_IA32_THERM_STATUS, &evattr_therm_snap, test_therm_status, }, |
---|
128 | | - [PERF_MSR_THERM_UNIT] = { MSR_IA32_THERM_STATUS, &evattr_therm_unit, test_therm_status, }, |
---|
| 144 | + [PERF_MSR_TSC] = { .no_check = true, }, |
---|
| 145 | + [PERF_MSR_APERF] = { MSR_IA32_APERF, &group_aperf, test_aperfmperf, }, |
---|
| 146 | + [PERF_MSR_MPERF] = { MSR_IA32_MPERF, &group_mperf, test_aperfmperf, }, |
---|
| 147 | + [PERF_MSR_PPERF] = { MSR_PPERF, &group_pperf, test_intel, }, |
---|
| 148 | + [PERF_MSR_SMI] = { MSR_SMI_COUNT, &group_smi, test_intel, }, |
---|
| 149 | + [PERF_MSR_PTSC] = { MSR_F15H_PTSC, &group_ptsc, test_ptsc, }, |
---|
| 150 | + [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF, &group_irperf, test_irperf, }, |
---|
| 151 | + [PERF_MSR_THERM] = { MSR_IA32_THERM_STATUS, &group_therm, test_therm_status, }, |
---|
129 | 152 | }; |
---|
130 | 153 | |
---|
131 | | -static struct attribute *events_attrs[PERF_MSR_EVENT_MAX + 1] = { |
---|
| 154 | +static struct attribute *events_attrs[] = { |
---|
| 155 | + &attr_tsc.attr.attr, |
---|
132 | 156 | NULL, |
---|
133 | 157 | }; |
---|
134 | 158 | |
---|
.. | .. |
---|
153 | 177 | NULL, |
---|
154 | 178 | }; |
---|
155 | 179 | |
---|
| 180 | +static const struct attribute_group *attr_update[] = { |
---|
| 181 | + &group_aperf, |
---|
| 182 | + &group_mperf, |
---|
| 183 | + &group_pperf, |
---|
| 184 | + &group_smi, |
---|
| 185 | + &group_ptsc, |
---|
| 186 | + &group_irperf, |
---|
| 187 | + &group_therm, |
---|
| 188 | + NULL, |
---|
| 189 | +}; |
---|
| 190 | + |
---|
156 | 191 | static int msr_event_init(struct perf_event *event) |
---|
157 | 192 | { |
---|
158 | 193 | u64 cfg = event->attr.config; |
---|
.. | .. |
---|
161 | 196 | return -ENOENT; |
---|
162 | 197 | |
---|
163 | 198 | /* unsupported modes and filters */ |
---|
164 | | - if (event->attr.exclude_user || |
---|
165 | | - event->attr.exclude_kernel || |
---|
166 | | - event->attr.exclude_hv || |
---|
167 | | - event->attr.exclude_idle || |
---|
168 | | - event->attr.exclude_host || |
---|
169 | | - event->attr.exclude_guest || |
---|
170 | | - event->attr.sample_period) /* no sampling */ |
---|
| 199 | + if (event->attr.sample_period) /* no sampling */ |
---|
171 | 200 | return -EINVAL; |
---|
172 | 201 | |
---|
173 | 202 | if (cfg >= PERF_MSR_EVENT_MAX) |
---|
.. | .. |
---|
175 | 204 | |
---|
176 | 205 | cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX); |
---|
177 | 206 | |
---|
178 | | - if (!msr[cfg].attr) |
---|
| 207 | + if (!(msr_mask & (1 << cfg))) |
---|
179 | 208 | return -EINVAL; |
---|
180 | 209 | |
---|
181 | 210 | event->hw.idx = -1; |
---|
.. | .. |
---|
257 | 286 | .start = msr_event_start, |
---|
258 | 287 | .stop = msr_event_stop, |
---|
259 | 288 | .read = msr_event_update, |
---|
260 | | - .capabilities = PERF_PMU_CAP_NO_INTERRUPT, |
---|
| 289 | + .capabilities = PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE, |
---|
| 290 | + .attr_update = attr_update, |
---|
261 | 291 | }; |
---|
262 | 292 | |
---|
263 | 293 | static int __init msr_init(void) |
---|
264 | 294 | { |
---|
265 | | - int i, j = 0; |
---|
266 | | - |
---|
267 | 295 | if (!boot_cpu_has(X86_FEATURE_TSC)) { |
---|
268 | 296 | pr_cont("no MSR PMU driver.\n"); |
---|
269 | 297 | return 0; |
---|
270 | 298 | } |
---|
271 | 299 | |
---|
272 | | - /* Probe the MSRs. */ |
---|
273 | | - for (i = PERF_MSR_TSC + 1; i < PERF_MSR_EVENT_MAX; i++) { |
---|
274 | | - u64 val; |
---|
275 | | - |
---|
276 | | - /* Virt sucks; you cannot tell if a R/O MSR is present :/ */ |
---|
277 | | - if (!msr[i].test(i) || rdmsrl_safe(msr[i].msr, &val)) |
---|
278 | | - msr[i].attr = NULL; |
---|
279 | | - } |
---|
280 | | - |
---|
281 | | - /* List remaining MSRs in the sysfs attrs. */ |
---|
282 | | - for (i = 0; i < PERF_MSR_EVENT_MAX; i++) { |
---|
283 | | - if (msr[i].attr) |
---|
284 | | - events_attrs[j++] = &msr[i].attr->attr.attr; |
---|
285 | | - } |
---|
286 | | - events_attrs[j] = NULL; |
---|
| 300 | + msr_mask = perf_msr_probe(msr, PERF_MSR_EVENT_MAX, true, NULL); |
---|
287 | 301 | |
---|
288 | 302 | perf_pmu_register(&pmu_msr, "msr", -1); |
---|
289 | 303 | |
---|