Commit | Line | Data |
---|---|---|
d4413732 | 1 | /* |
6852fd9b | 2 | * @file op_model_amd.c |
bd87f1f0 | 3 | * athlon / K7 / K8 / Family 10h model-specific MSR operations |
1da177e4 | 4 | * |
adf5ec0b | 5 | * @remark Copyright 2002-2008 OProfile authors |
1da177e4 LT |
6 | * @remark Read the file COPYING |
7 | * | |
8 | * @author John Levon | |
9 | * @author Philippe Elie | |
10 | * @author Graydon Hoare | |
adf5ec0b | 11 | * @author Robert Richter <robert.richter@amd.com> |
56784f11 | 12 | * @author Barry Kasindorf |
adf5ec0b | 13 | */ |
1da177e4 LT |
14 | |
15 | #include <linux/oprofile.h> | |
56784f11 BK |
16 | #include <linux/device.h> |
17 | #include <linux/pci.h> | |
18 | ||
1da177e4 LT |
19 | #include <asm/ptrace.h> |
20 | #include <asm/msr.h> | |
3e4ff115 | 21 | #include <asm/nmi.h> |
d4413732 | 22 | |
1da177e4 LT |
23 | #include "op_x86_model.h" |
24 | #include "op_counter.h" | |
25 | ||
4c168eaf RR |
26 | #define NUM_COUNTERS 4 |
27 | #define NUM_CONTROLS 4 | |
1da177e4 | 28 | |
d4413732 PC |
29 | #define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) |
30 | #define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) | |
31 | #define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) | |
1da177e4 LT |
32 | #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) |
33 | ||
d4413732 PC |
34 | #define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) |
35 | #define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
36 | #define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) | |
1da177e4 LT |
37 | #define CTRL_SET_ACTIVE(n) (n |= (1<<22)) |
38 | #define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) | |
bd87f1f0 BK |
39 | #define CTRL_CLEAR_LO(x) (x &= (1<<21)) |
40 | #define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) | |
1da177e4 | 41 | #define CTRL_SET_ENABLE(val) (val |= 1<<20) |
d4413732 PC |
42 | #define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) |
43 | #define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) | |
1da177e4 | 44 | #define CTRL_SET_UM(val, m) (val |= (m << 8)) |
bd87f1f0 BK |
45 | #define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) |
46 | #define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) | |
47 | #define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) | |
48 | #define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) | |
1da177e4 | 49 | |
852402cc RR |
50 | static unsigned long reset_value[NUM_COUNTERS]; |
51 | ||
52 | #ifdef CONFIG_OPROFILE_IBS | |
53 | ||
87f0bacc RR |
54 | /* IbsFetchCtl bits/masks */ |
55 | #define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */ | |
56 | #define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */ | |
57 | #define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */ | |
56784f11 | 58 | |
87f0bacc RR |
59 | /*IbsOpCtl bits */ |
60 | #define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ | |
61 | #define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ | |
56784f11 BK |
62 | |
63 | /* Codes used in cpu_buffer.c */ | |
87f0bacc | 64 | /* This produces duplicate code, need to be fixed */ |
56784f11 BK |
65 | #define IBS_FETCH_BEGIN 3 |
66 | #define IBS_OP_BEGIN 4 | |
67 | ||
fd13f6c8 RR |
68 | /* |
69 | * The function interface needs to be fixed, something like add | |
70 | * data. Should then be added to linux/oprofile.h. | |
71 | */ | |
e2fee276 | 72 | extern void |
cdc1834d RR |
73 | oprofile_add_ibs_sample(struct pt_regs * const regs, |
74 | unsigned int * const ibs_sample, int ibs_code); | |
90645700 | 75 | |
56784f11 BK |
76 | struct ibs_fetch_sample { |
77 | /* MSRC001_1031 IBS Fetch Linear Address Register */ | |
78 | unsigned int ibs_fetch_lin_addr_low; | |
79 | unsigned int ibs_fetch_lin_addr_high; | |
80 | /* MSRC001_1030 IBS Fetch Control Register */ | |
81 | unsigned int ibs_fetch_ctl_low; | |
82 | unsigned int ibs_fetch_ctl_high; | |
83 | /* MSRC001_1032 IBS Fetch Physical Address Register */ | |
84 | unsigned int ibs_fetch_phys_addr_low; | |
85 | unsigned int ibs_fetch_phys_addr_high; | |
86 | }; | |
87 | ||
88 | struct ibs_op_sample { | |
89 | /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ | |
90 | unsigned int ibs_op_rip_low; | |
91 | unsigned int ibs_op_rip_high; | |
92 | /* MSRC001_1035 IBS Op Data Register */ | |
93 | unsigned int ibs_op_data1_low; | |
94 | unsigned int ibs_op_data1_high; | |
95 | /* MSRC001_1036 IBS Op Data 2 Register */ | |
96 | unsigned int ibs_op_data2_low; | |
97 | unsigned int ibs_op_data2_high; | |
98 | /* MSRC001_1037 IBS Op Data 3 Register */ | |
99 | unsigned int ibs_op_data3_low; | |
100 | unsigned int ibs_op_data3_high; | |
101 | /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ | |
102 | unsigned int ibs_dc_linear_low; | |
103 | unsigned int ibs_dc_linear_high; | |
104 | /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ | |
105 | unsigned int ibs_dc_phys_low; | |
106 | unsigned int ibs_dc_phys_high; | |
107 | }; | |
108 | ||
109 | /* | |
110 | * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ | |
fd13f6c8 | 111 | */ |
56784f11 BK |
112 | static void clear_ibs_nmi(void); |
113 | ||
56784f11 BK |
114 | static int ibs_allowed; /* AMD Family10h and later */ |
115 | ||
116 | struct op_ibs_config { | |
117 | unsigned long op_enabled; | |
118 | unsigned long fetch_enabled; | |
119 | unsigned long max_cnt_fetch; | |
120 | unsigned long max_cnt_op; | |
121 | unsigned long rand_en; | |
122 | unsigned long dispatched_ops; | |
123 | }; | |
124 | ||
125 | static struct op_ibs_config ibs_config; | |
d4413732 | 126 | |
852402cc RR |
127 | #endif |
128 | ||
6657fe4f | 129 | /* functions for op_amd_spec */ |
dfa15428 | 130 | |
6657fe4f | 131 | static void op_amd_fill_in_addresses(struct op_msrs * const msrs) |
1da177e4 | 132 | { |
cb9c448c DZ |
133 | int i; |
134 | ||
d4413732 | 135 | for (i = 0; i < NUM_COUNTERS; i++) { |
4c168eaf RR |
136 | if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) |
137 | msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | |
cb9c448c DZ |
138 | else |
139 | msrs->counters[i].addr = 0; | |
140 | } | |
141 | ||
d4413732 | 142 | for (i = 0; i < NUM_CONTROLS; i++) { |
4c168eaf RR |
143 | if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) |
144 | msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | |
cb9c448c DZ |
145 | else |
146 | msrs->controls[i].addr = 0; | |
147 | } | |
1da177e4 LT |
148 | } |
149 | ||
d4413732 | 150 | |
6657fe4f | 151 | static void op_amd_setup_ctrs(struct op_msrs const * const msrs) |
1da177e4 LT |
152 | { |
153 | unsigned int low, high; | |
154 | int i; | |
d4413732 | 155 | |
1da177e4 | 156 | /* clear all counters */ |
4c168eaf | 157 | for (i = 0 ; i < NUM_CONTROLS; ++i) { |
d4413732 | 158 | if (unlikely(!CTRL_IS_RESERVED(msrs, i))) |
cb9c448c | 159 | continue; |
1da177e4 | 160 | CTRL_READ(low, high, msrs, i); |
bd87f1f0 BK |
161 | CTRL_CLEAR_LO(low); |
162 | CTRL_CLEAR_HI(high); | |
1da177e4 LT |
163 | CTRL_WRITE(low, high, msrs, i); |
164 | } | |
cb9c448c | 165 | |
1da177e4 | 166 | /* avoid a false detection of ctr overflows in NMI handler */ |
4c168eaf | 167 | for (i = 0; i < NUM_COUNTERS; ++i) { |
d4413732 | 168 | if (unlikely(!CTR_IS_RESERVED(msrs, i))) |
cb9c448c | 169 | continue; |
1da177e4 LT |
170 | CTR_WRITE(1, msrs, i); |
171 | } | |
172 | ||
173 | /* enable active counters */ | |
4c168eaf RR |
174 | for (i = 0; i < NUM_COUNTERS; ++i) { |
175 | if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { | |
176 | reset_value[i] = counter_config[i].count; | |
177 | ||
178 | CTR_WRITE(counter_config[i].count, msrs, i); | |
1da177e4 LT |
179 | |
180 | CTRL_READ(low, high, msrs, i); | |
bd87f1f0 BK |
181 | CTRL_CLEAR_LO(low); |
182 | CTRL_CLEAR_HI(high); | |
1da177e4 | 183 | CTRL_SET_ENABLE(low); |
4c168eaf RR |
184 | CTRL_SET_USR(low, counter_config[i].user); |
185 | CTRL_SET_KERN(low, counter_config[i].kernel); | |
186 | CTRL_SET_UM(low, counter_config[i].unit_mask); | |
187 | CTRL_SET_EVENT_LOW(low, counter_config[i].event); | |
188 | CTRL_SET_EVENT_HIGH(high, counter_config[i].event); | |
bd87f1f0 BK |
189 | CTRL_SET_HOST_ONLY(high, 0); |
190 | CTRL_SET_GUEST_ONLY(high, 0); | |
191 | ||
1da177e4 | 192 | CTRL_WRITE(low, high, msrs, i); |
4c168eaf RR |
193 | } else { |
194 | reset_value[i] = 0; | |
1da177e4 LT |
195 | } |
196 | } | |
197 | } | |
198 | ||
852402cc RR |
199 | #ifdef CONFIG_OPROFILE_IBS |
200 | ||
7939d2bf RR |
201 | static inline int |
202 | op_amd_handle_ibs(struct pt_regs * const regs, | |
203 | struct op_msrs const * const msrs) | |
1da177e4 LT |
204 | { |
205 | unsigned int low, high; | |
56784f11 BK |
206 | struct ibs_fetch_sample ibs_fetch; |
207 | struct ibs_op_sample ibs_op; | |
1da177e4 | 208 | |
7939d2bf RR |
209 | if (!ibs_allowed) |
210 | return 1; | |
1da177e4 | 211 | |
7939d2bf | 212 | if (ibs_config.fetch_enabled) { |
56784f11 | 213 | rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); |
87f0bacc | 214 | if (high & IBS_FETCH_HIGH_VALID_BIT) { |
56784f11 BK |
215 | ibs_fetch.ibs_fetch_ctl_high = high; |
216 | ibs_fetch.ibs_fetch_ctl_low = low; | |
217 | rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); | |
218 | ibs_fetch.ibs_fetch_lin_addr_high = high; | |
219 | ibs_fetch.ibs_fetch_lin_addr_low = low; | |
220 | rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); | |
221 | ibs_fetch.ibs_fetch_phys_addr_high = high; | |
222 | ibs_fetch.ibs_fetch_phys_addr_low = low; | |
223 | ||
224 | oprofile_add_ibs_sample(regs, | |
225 | (unsigned int *)&ibs_fetch, | |
226 | IBS_FETCH_BEGIN); | |
227 | ||
fd13f6c8 | 228 | /* reenable the IRQ */ |
56784f11 | 229 | rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); |
87f0bacc RR |
230 | high &= ~IBS_FETCH_HIGH_VALID_BIT; |
231 | high |= IBS_FETCH_HIGH_ENABLE; | |
232 | low &= IBS_FETCH_LOW_MAX_CNT_MASK; | |
56784f11 BK |
233 | wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); |
234 | } | |
235 | } | |
236 | ||
7939d2bf | 237 | if (ibs_config.op_enabled) { |
56784f11 | 238 | rdmsr(MSR_AMD64_IBSOPCTL, low, high); |
87f0bacc | 239 | if (low & IBS_OP_LOW_VALID_BIT) { |
56784f11 BK |
240 | rdmsr(MSR_AMD64_IBSOPRIP, low, high); |
241 | ibs_op.ibs_op_rip_low = low; | |
242 | ibs_op.ibs_op_rip_high = high; | |
243 | rdmsr(MSR_AMD64_IBSOPDATA, low, high); | |
244 | ibs_op.ibs_op_data1_low = low; | |
245 | ibs_op.ibs_op_data1_high = high; | |
246 | rdmsr(MSR_AMD64_IBSOPDATA2, low, high); | |
247 | ibs_op.ibs_op_data2_low = low; | |
248 | ibs_op.ibs_op_data2_high = high; | |
249 | rdmsr(MSR_AMD64_IBSOPDATA3, low, high); | |
250 | ibs_op.ibs_op_data3_low = low; | |
251 | ibs_op.ibs_op_data3_high = high; | |
252 | rdmsr(MSR_AMD64_IBSDCLINAD, low, high); | |
253 | ibs_op.ibs_dc_linear_low = low; | |
254 | ibs_op.ibs_dc_linear_high = high; | |
255 | rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); | |
256 | ibs_op.ibs_dc_phys_low = low; | |
257 | ibs_op.ibs_dc_phys_high = high; | |
258 | ||
259 | /* reenable the IRQ */ | |
260 | oprofile_add_ibs_sample(regs, | |
261 | (unsigned int *)&ibs_op, | |
262 | IBS_OP_BEGIN); | |
263 | rdmsr(MSR_AMD64_IBSOPCTL, low, high); | |
543a157b | 264 | high = 0; |
87f0bacc RR |
265 | low &= ~IBS_OP_LOW_VALID_BIT; |
266 | low |= IBS_OP_LOW_ENABLE; | |
56784f11 BK |
267 | wrmsr(MSR_AMD64_IBSOPCTL, low, high); |
268 | } | |
269 | } | |
270 | ||
1da177e4 LT |
271 | return 1; |
272 | } | |
273 | ||
852402cc RR |
274 | #endif |
275 | ||
7939d2bf RR |
276 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
277 | struct op_msrs const * const msrs) | |
278 | { | |
279 | unsigned int low, high; | |
280 | int i; | |
281 | ||
4c168eaf RR |
282 | for (i = 0 ; i < NUM_COUNTERS; ++i) { |
283 | if (!reset_value[i]) | |
7939d2bf RR |
284 | continue; |
285 | CTR_READ(low, high, msrs, i); | |
286 | if (CTR_OVERFLOWED(low)) { | |
4c168eaf RR |
287 | oprofile_add_sample(regs, i); |
288 | CTR_WRITE(reset_value[i], msrs, i); | |
7939d2bf RR |
289 | } |
290 | } | |
291 | ||
852402cc | 292 | #ifdef CONFIG_OPROFILE_IBS |
7939d2bf | 293 | op_amd_handle_ibs(regs, msrs); |
852402cc | 294 | #endif |
7939d2bf RR |
295 | |
296 | /* See op_model_ppro.c */ | |
297 | return 1; | |
298 | } | |
d4413732 | 299 | |
6657fe4f | 300 | static void op_amd_start(struct op_msrs const * const msrs) |
1da177e4 LT |
301 | { |
302 | unsigned int low, high; | |
303 | int i; | |
4c168eaf RR |
304 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
305 | if (reset_value[i]) { | |
1da177e4 LT |
306 | CTRL_READ(low, high, msrs, i); |
307 | CTRL_SET_ACTIVE(low); | |
308 | CTRL_WRITE(low, high, msrs, i); | |
309 | } | |
310 | } | |
852402cc RR |
311 | |
312 | #ifdef CONFIG_OPROFILE_IBS | |
56784f11 BK |
313 | if (ibs_allowed && ibs_config.fetch_enabled) { |
314 | low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; | |
5f87dfb7 SS |
315 | high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */ |
316 | + IBS_FETCH_HIGH_ENABLE; | |
56784f11 BK |
317 | wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); |
318 | } | |
319 | ||
320 | if (ibs_allowed && ibs_config.op_enabled) { | |
5f87dfb7 SS |
321 | low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) |
322 | + ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */ | |
323 | + IBS_OP_LOW_ENABLE; | |
56784f11 BK |
324 | high = 0; |
325 | wrmsr(MSR_AMD64_IBSOPCTL, low, high); | |
326 | } | |
852402cc | 327 | #endif |
1da177e4 LT |
328 | } |
329 | ||
330 | ||
6657fe4f | 331 | static void op_amd_stop(struct op_msrs const * const msrs) |
1da177e4 | 332 | { |
d4413732 | 333 | unsigned int low, high; |
1da177e4 LT |
334 | int i; |
335 | ||
fd13f6c8 RR |
336 | /* |
337 | * Subtle: stop on all counters to avoid race with setting our | |
338 | * pm callback | |
339 | */ | |
4c168eaf RR |
340 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
341 | if (!reset_value[i]) | |
cb9c448c | 342 | continue; |
1da177e4 LT |
343 | CTRL_READ(low, high, msrs, i); |
344 | CTRL_SET_INACTIVE(low); | |
345 | CTRL_WRITE(low, high, msrs, i); | |
346 | } | |
56784f11 | 347 | |
852402cc | 348 | #ifdef CONFIG_OPROFILE_IBS |
56784f11 | 349 | if (ibs_allowed && ibs_config.fetch_enabled) { |
fd13f6c8 RR |
350 | /* clear max count and enable */ |
351 | low = 0; | |
56784f11 BK |
352 | high = 0; |
353 | wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); | |
354 | } | |
355 | ||
356 | if (ibs_allowed && ibs_config.op_enabled) { | |
fd13f6c8 RR |
357 | /* clear max count and enable */ |
358 | low = 0; | |
56784f11 BK |
359 | high = 0; |
360 | wrmsr(MSR_AMD64_IBSOPCTL, low, high); | |
361 | } | |
852402cc | 362 | #endif |
1da177e4 LT |
363 | } |
364 | ||
6657fe4f | 365 | static void op_amd_shutdown(struct op_msrs const * const msrs) |
cb9c448c DZ |
366 | { |
367 | int i; | |
368 | ||
4c168eaf | 369 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
d4413732 | 370 | if (CTR_IS_RESERVED(msrs, i)) |
cb9c448c DZ |
371 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); |
372 | } | |
4c168eaf | 373 | for (i = 0 ; i < NUM_CONTROLS ; ++i) { |
d4413732 | 374 | if (CTRL_IS_RESERVED(msrs, i)) |
cb9c448c DZ |
375 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); |
376 | } | |
377 | } | |
1da177e4 | 378 | |
9fa6812d | 379 | #ifdef CONFIG_OPROFILE_IBS |
a4c408a4 | 380 | |
7d77f2dc RR |
381 | static u8 ibs_eilvt_off; |
382 | ||
56784f11 BK |
383 | static inline void apic_init_ibs_nmi_per_cpu(void *arg) |
384 | { | |
7d77f2dc | 385 | ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); |
56784f11 BK |
386 | } |
387 | ||
388 | static inline void apic_clear_ibs_nmi_per_cpu(void *arg) | |
389 | { | |
390 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | |
391 | } | |
392 | ||
7d77f2dc RR |
393 | static int pfm_amd64_setup_eilvt(void) |
394 | { | |
395 | #define IBSCTL_LVTOFFSETVAL (1 << 8) | |
396 | #define IBSCTL 0x1cc | |
397 | struct pci_dev *cpu_cfg; | |
398 | int nodes; | |
399 | u32 value = 0; | |
400 | ||
401 | /* per CPU setup */ | |
ebb535de | 402 | on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); |
7d77f2dc RR |
403 | |
404 | nodes = 0; | |
405 | cpu_cfg = NULL; | |
406 | do { | |
407 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | |
408 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | |
409 | cpu_cfg); | |
410 | if (!cpu_cfg) | |
411 | break; | |
412 | ++nodes; | |
413 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | |
414 | | IBSCTL_LVTOFFSETVAL); | |
415 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | |
416 | if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | |
417 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | |
418 | "IBSCTL = 0x%08x", value); | |
419 | return 1; | |
420 | } | |
421 | } while (1); | |
422 | ||
423 | if (!nodes) { | |
424 | printk(KERN_DEBUG "No CPU node configured for IBS"); | |
425 | return 1; | |
426 | } | |
427 | ||
428 | #ifdef CONFIG_NUMA | |
429 | /* Sanity check */ | |
430 | /* Works only for 64bit with proper numa implementation. */ | |
431 | if (nodes != num_possible_nodes()) { | |
432 | printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, " | |
433 | "found: %d, expected %d", | |
434 | nodes, num_possible_nodes()); | |
435 | return 1; | |
436 | } | |
437 | #endif | |
438 | return 0; | |
439 | } | |
440 | ||
fd13f6c8 | 441 | /* initialize the APIC for the IBS interrupts if available */ |
56784f11 BK |
442 | static void setup_ibs(void) |
443 | { | |
56784f11 BK |
444 | ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); |
445 | ||
446 | if (!ibs_allowed) | |
447 | return; | |
448 | ||
852402cc | 449 | if (pfm_amd64_setup_eilvt()) { |
7d77f2dc | 450 | ibs_allowed = 0; |
852402cc RR |
451 | return; |
452 | } | |
453 | ||
454 | printk(KERN_INFO "oprofile: AMD IBS detected\n"); | |
56784f11 BK |
455 | } |
456 | ||
457 | ||
fd13f6c8 | 458 | /* uninitialize the APIC for the IBS interrupts if needed */ |
56784f11 BK |
459 | static void clear_ibs_nmi(void) |
460 | { | |
461 | if (ibs_allowed) | |
ebb535de | 462 | on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); |
56784f11 BK |
463 | } |
464 | ||
25ad2913 | 465 | static int (*create_arch_files)(struct super_block *sb, struct dentry *root); |
270d3e1a | 466 | |
25ad2913 | 467 | static int setup_ibs_files(struct super_block *sb, struct dentry *root) |
56784f11 | 468 | { |
56784f11 | 469 | struct dentry *dir; |
270d3e1a RR |
470 | int ret = 0; |
471 | ||
472 | /* architecture specific files */ | |
473 | if (create_arch_files) | |
474 | ret = create_arch_files(sb, root); | |
475 | ||
476 | if (ret) | |
477 | return ret; | |
56784f11 BK |
478 | |
479 | if (!ibs_allowed) | |
270d3e1a RR |
480 | return ret; |
481 | ||
482 | /* model specific files */ | |
56784f11 BK |
483 | |
484 | /* setup some reasonable defaults */ | |
485 | ibs_config.max_cnt_fetch = 250000; | |
486 | ibs_config.fetch_enabled = 0; | |
487 | ibs_config.max_cnt_op = 250000; | |
488 | ibs_config.op_enabled = 0; | |
489 | ibs_config.dispatched_ops = 1; | |
2d55a478 RR |
490 | |
491 | dir = oprofilefs_mkdir(sb, root, "ibs_fetch"); | |
56784f11 | 492 | oprofilefs_create_ulong(sb, dir, "enable", |
2d55a478 | 493 | &ibs_config.fetch_enabled); |
56784f11 | 494 | oprofilefs_create_ulong(sb, dir, "max_count", |
2d55a478 RR |
495 | &ibs_config.max_cnt_fetch); |
496 | oprofilefs_create_ulong(sb, dir, "rand_enable", | |
497 | &ibs_config.rand_en); | |
498 | ||
ccd755c2 | 499 | dir = oprofilefs_mkdir(sb, root, "ibs_op"); |
56784f11 | 500 | oprofilefs_create_ulong(sb, dir, "enable", |
2d55a478 | 501 | &ibs_config.op_enabled); |
56784f11 | 502 | oprofilefs_create_ulong(sb, dir, "max_count", |
2d55a478 | 503 | &ibs_config.max_cnt_op); |
56784f11 | 504 | oprofilefs_create_ulong(sb, dir, "dispatched_ops", |
2d55a478 | 505 | &ibs_config.dispatched_ops); |
fc2bd734 RR |
506 | |
507 | return 0; | |
56784f11 BK |
508 | } |
509 | ||
adf5ec0b RR |
510 | static int op_amd_init(struct oprofile_operations *ops) |
511 | { | |
270d3e1a RR |
512 | setup_ibs(); |
513 | create_arch_files = ops->create_files; | |
514 | ops->create_files = setup_ibs_files; | |
adf5ec0b RR |
515 | return 0; |
516 | } | |
517 | ||
518 | static void op_amd_exit(void) | |
519 | { | |
270d3e1a | 520 | clear_ibs_nmi(); |
adf5ec0b RR |
521 | } |
522 | ||
9fa6812d RR |
523 | #else |
524 | ||
525 | /* no IBS support */ | |
526 | ||
527 | static int op_amd_init(struct oprofile_operations *ops) | |
528 | { | |
529 | return 0; | |
530 | } | |
531 | ||
532 | static void op_amd_exit(void) {} | |
533 | ||
534 | #endif /* CONFIG_OPROFILE_IBS */ | |
a4c408a4 | 535 | |
6657fe4f | 536 | struct op_x86_model_spec const op_amd_spec = { |
c92960fc RR |
537 | .init = op_amd_init, |
538 | .exit = op_amd_exit, | |
539 | .num_counters = NUM_COUNTERS, | |
540 | .num_controls = NUM_CONTROLS, | |
541 | .fill_in_addresses = &op_amd_fill_in_addresses, | |
542 | .setup_ctrs = &op_amd_setup_ctrs, | |
543 | .check_ctrs = &op_amd_check_ctrs, | |
544 | .start = &op_amd_start, | |
545 | .stop = &op_amd_stop, | |
546 | .shutdown = &op_amd_shutdown | |
1da177e4 | 547 | }; |