Commit | Line | Data |
---|---|---|
b7b7c782 AL |
1 | #include <linux/perf_event.h> |
2 | ||
3 | enum perf_msr_id { | |
4 | PERF_MSR_TSC = 0, | |
5 | PERF_MSR_APERF = 1, | |
6 | PERF_MSR_MPERF = 2, | |
7 | PERF_MSR_PPERF = 3, | |
8 | PERF_MSR_SMI = 4, | |
9 | ||
10 | PERF_MSR_EVENT_MAX, | |
11 | }; | |
12 | ||
13 | struct perf_msr { | |
14 | int id; | |
15 | u64 msr; | |
16 | }; | |
17 | ||
18 | static struct perf_msr msr[] = { | |
19 | { PERF_MSR_TSC, 0 }, | |
20 | { PERF_MSR_APERF, MSR_IA32_APERF }, | |
21 | { PERF_MSR_MPERF, MSR_IA32_MPERF }, | |
22 | { PERF_MSR_PPERF, MSR_PPERF }, | |
23 | { PERF_MSR_SMI, MSR_SMI_COUNT }, | |
24 | }; | |
25 | ||
26 | PMU_EVENT_ATTR_STRING(tsc, evattr_tsc, "event=0x00"); | |
27 | PMU_EVENT_ATTR_STRING(aperf, evattr_aperf, "event=0x01"); | |
28 | PMU_EVENT_ATTR_STRING(mperf, evattr_mperf, "event=0x02"); | |
29 | PMU_EVENT_ATTR_STRING(pperf, evattr_pperf, "event=0x03"); | |
30 | PMU_EVENT_ATTR_STRING(smi, evattr_smi, "event=0x04"); | |
31 | ||
32 | static struct attribute *events_attrs[PERF_MSR_EVENT_MAX + 1] = { | |
33 | &evattr_tsc.attr.attr, | |
34 | }; | |
35 | ||
36 | static struct attribute_group events_attr_group = { | |
37 | .name = "events", | |
38 | .attrs = events_attrs, | |
39 | }; | |
40 | ||
41 | PMU_FORMAT_ATTR(event, "config:0-63"); | |
42 | static struct attribute *format_attrs[] = { | |
43 | &format_attr_event.attr, | |
44 | NULL, | |
45 | }; | |
46 | static struct attribute_group format_attr_group = { | |
47 | .name = "format", | |
48 | .attrs = format_attrs, | |
49 | }; | |
50 | ||
51 | static const struct attribute_group *attr_groups[] = { | |
52 | &events_attr_group, | |
53 | &format_attr_group, | |
54 | NULL, | |
55 | }; | |
56 | ||
57 | static int msr_event_init(struct perf_event *event) | |
58 | { | |
59 | u64 cfg = event->attr.config; | |
60 | ||
61 | if (event->attr.type != event->pmu->type) | |
62 | return -ENOENT; | |
63 | ||
64 | if (cfg >= PERF_MSR_EVENT_MAX) | |
65 | return -EINVAL; | |
66 | ||
67 | /* unsupported modes and filters */ | |
68 | if (event->attr.exclude_user || | |
69 | event->attr.exclude_kernel || | |
70 | event->attr.exclude_hv || | |
71 | event->attr.exclude_idle || | |
72 | event->attr.exclude_host || | |
73 | event->attr.exclude_guest || | |
74 | event->attr.sample_period) /* no sampling */ | |
75 | return -EINVAL; | |
76 | ||
77 | event->hw.idx = -1; | |
78 | event->hw.event_base = msr[cfg].msr; | |
79 | event->hw.config = cfg; | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static inline u64 msr_read_counter(struct perf_event *event) | |
85 | { | |
86 | u64 now; | |
87 | ||
88 | if (event->hw.event_base) | |
89 | rdmsrl(event->hw.event_base, now); | |
90 | else | |
91 | now = rdtsc(); | |
92 | ||
93 | return now; | |
94 | } | |
95 | static void msr_event_update(struct perf_event *event) | |
96 | { | |
97 | u64 prev, now; | |
98 | s64 delta; | |
99 | ||
100 | /* Careful, an NMI might modify the previous event value. */ | |
101 | again: | |
102 | prev = local64_read(&event->hw.prev_count); | |
103 | now = msr_read_counter(event); | |
104 | ||
105 | if (local64_cmpxchg(&event->hw.prev_count, prev, now) != prev) | |
106 | goto again; | |
107 | ||
108 | delta = now - prev; | |
109 | if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) { | |
110 | delta <<= 32; | |
111 | delta >>= 32; /* sign extend */ | |
112 | } | |
113 | local64_add(now - prev, &event->count); | |
114 | } | |
115 | ||
116 | static void msr_event_start(struct perf_event *event, int flags) | |
117 | { | |
118 | u64 now; | |
119 | ||
120 | now = msr_read_counter(event); | |
121 | local64_set(&event->hw.prev_count, now); | |
122 | } | |
123 | ||
124 | static void msr_event_stop(struct perf_event *event, int flags) | |
125 | { | |
126 | msr_event_update(event); | |
127 | } | |
128 | ||
129 | static void msr_event_del(struct perf_event *event, int flags) | |
130 | { | |
131 | msr_event_stop(event, PERF_EF_UPDATE); | |
132 | } | |
133 | ||
134 | static int msr_event_add(struct perf_event *event, int flags) | |
135 | { | |
136 | if (flags & PERF_EF_START) | |
137 | msr_event_start(event, flags); | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | static struct pmu pmu_msr = { | |
143 | .task_ctx_nr = perf_sw_context, | |
144 | .attr_groups = attr_groups, | |
145 | .event_init = msr_event_init, | |
146 | .add = msr_event_add, | |
147 | .del = msr_event_del, | |
148 | .start = msr_event_start, | |
149 | .stop = msr_event_stop, | |
150 | .read = msr_event_update, | |
151 | .capabilities = PERF_PMU_CAP_NO_INTERRUPT, | |
152 | }; | |
153 | ||
154 | static int __init intel_msr_init(int idx) | |
155 | { | |
156 | if (boot_cpu_data.x86 != 6) | |
157 | return 0; | |
158 | ||
159 | switch (boot_cpu_data.x86_model) { | |
160 | case 30: /* 45nm Nehalem */ | |
161 | case 26: /* 45nm Nehalem-EP */ | |
162 | case 46: /* 45nm Nehalem-EX */ | |
163 | ||
164 | case 37: /* 32nm Westmere */ | |
165 | case 44: /* 32nm Westmere-EP */ | |
166 | case 47: /* 32nm Westmere-EX */ | |
167 | ||
168 | case 42: /* 32nm SandyBridge */ | |
169 | case 45: /* 32nm SandyBridge-E/EN/EP */ | |
170 | ||
171 | case 58: /* 22nm IvyBridge */ | |
172 | case 62: /* 22nm IvyBridge-EP/EX */ | |
173 | ||
174 | case 60: /* 22nm Haswell Core */ | |
175 | case 63: /* 22nm Haswell Server */ | |
176 | case 69: /* 22nm Haswell ULT */ | |
177 | case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */ | |
178 | ||
179 | case 61: /* 14nm Broadwell Core-M */ | |
180 | case 86: /* 14nm Broadwell Xeon D */ | |
181 | case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */ | |
182 | case 79: /* 14nm Broadwell Server */ | |
183 | events_attrs[idx++] = &evattr_smi.attr.attr; | |
184 | break; | |
185 | ||
186 | case 78: /* 14nm Skylake Mobile */ | |
187 | case 94: /* 14nm Skylake Desktop */ | |
188 | events_attrs[idx++] = &evattr_pperf.attr.attr; | |
189 | events_attrs[idx++] = &evattr_smi.attr.attr; | |
190 | break; | |
191 | ||
192 | case 55: /* 22nm Atom "Silvermont" */ | |
193 | case 76: /* 14nm Atom "Airmont" */ | |
194 | case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */ | |
195 | events_attrs[idx++] = &evattr_smi.attr.attr; | |
196 | break; | |
197 | } | |
198 | ||
199 | events_attrs[idx] = NULL; | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | static int __init amd_msr_init(int idx) | |
205 | { | |
206 | return 0; | |
207 | } | |
208 | ||
209 | static int __init msr_init(void) | |
210 | { | |
211 | int err; | |
212 | int idx = 1; | |
213 | ||
214 | if (boot_cpu_has(X86_FEATURE_APERFMPERF)) { | |
215 | events_attrs[idx++] = &evattr_aperf.attr.attr; | |
216 | events_attrs[idx++] = &evattr_mperf.attr.attr; | |
217 | events_attrs[idx] = NULL; | |
218 | } | |
219 | ||
220 | switch (boot_cpu_data.x86_vendor) { | |
221 | case X86_VENDOR_INTEL: | |
222 | err = intel_msr_init(idx); | |
223 | break; | |
224 | ||
225 | case X86_VENDOR_AMD: | |
226 | err = amd_msr_init(idx); | |
227 | break; | |
228 | ||
229 | default: | |
230 | err = -ENOTSUPP; | |
231 | } | |
232 | ||
233 | if (err != 0) { | |
234 | pr_cont("no msr PMU driver.\n"); | |
235 | return 0; | |
236 | } | |
237 | ||
238 | perf_pmu_register(&pmu_msr, "msr", -1); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | device_initcall(msr_init); |