]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/x86/oprofile/op_model_amd.c
x86: oprofile/op_model_amd.c set return values for op_amd_handle_ibs()
[mirror_ubuntu-artful-kernel.git] / arch / x86 / oprofile / op_model_amd.c
CommitLineData
d4413732 1/*
6852fd9b 2 * @file op_model_amd.c
bd87f1f0 3 * athlon / K7 / K8 / Family 10h model-specific MSR operations
1da177e4 4 *
ae735e99 5 * @remark Copyright 2002-2009 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
ae735e99 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
3370d358 28#define OP_EVENT_MASK 0x0FFF
42399adb 29#define OP_CTR_OVERFLOW (1ULL<<31)
3370d358
RR
30
31#define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21))
1da177e4 32
852402cc
RR
33static unsigned long reset_value[NUM_COUNTERS];
34
35#ifdef CONFIG_OPROFILE_IBS
36
87f0bacc 37/* IbsFetchCtl bits/masks */
c572ae4e
RR
38#define IBS_FETCH_RAND_EN (1ULL<<57)
39#define IBS_FETCH_VAL (1ULL<<49)
40#define IBS_FETCH_ENABLE (1ULL<<48)
41#define IBS_FETCH_CNT_MASK 0xFFFF0000ULL
56784f11 42
87f0bacc 43/*IbsOpCtl bits */
c572ae4e
RR
44#define IBS_OP_CNT_CTL (1ULL<<19)
45#define IBS_OP_VAL (1ULL<<18)
46#define IBS_OP_ENABLE (1ULL<<17)
56784f11 47
c572ae4e
RR
48#define IBS_FETCH_SIZE 6
49#define IBS_OP_SIZE 12
56784f11 50
fc81be8c 51static int has_ibs; /* AMD Family10h and later */
56784f11
BK
52
53struct op_ibs_config {
54 unsigned long op_enabled;
55 unsigned long fetch_enabled;
56 unsigned long max_cnt_fetch;
57 unsigned long max_cnt_op;
58 unsigned long rand_en;
59 unsigned long dispatched_ops;
60};
61
62static struct op_ibs_config ibs_config;
d4413732 63
852402cc
RR
64#endif
65
6657fe4f 66/* functions for op_amd_spec */
dfa15428 67
6657fe4f 68static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
1da177e4 69{
cb9c448c
DZ
70 int i;
71
d4413732 72 for (i = 0; i < NUM_COUNTERS; i++) {
4c168eaf
RR
73 if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
74 msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
cb9c448c
DZ
75 else
76 msrs->counters[i].addr = 0;
77 }
78
d4413732 79 for (i = 0; i < NUM_CONTROLS; i++) {
4c168eaf
RR
80 if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
81 msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
cb9c448c
DZ
82 else
83 msrs->controls[i].addr = 0;
84 }
1da177e4
LT
85}
86
ef8828dd
RR
87static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
88 struct op_msrs const * const msrs)
1da177e4 89{
3370d358 90 u64 val;
1da177e4 91 int i;
d4413732 92
1da177e4 93 /* clear all counters */
4c168eaf 94 for (i = 0 ; i < NUM_CONTROLS; ++i) {
217d3cfb 95 if (unlikely(!msrs->controls[i].addr))
cb9c448c 96 continue;
3370d358
RR
97 rdmsrl(msrs->controls[i].addr, val);
98 val &= model->reserved;
99 wrmsrl(msrs->controls[i].addr, val);
1da177e4 100 }
cb9c448c 101
1da177e4 102 /* avoid a false detection of ctr overflows in NMI handler */
4c168eaf 103 for (i = 0; i < NUM_COUNTERS; ++i) {
217d3cfb 104 if (unlikely(!msrs->counters[i].addr))
cb9c448c 105 continue;
bbc5986d 106 wrmsrl(msrs->counters[i].addr, -1LL);
1da177e4
LT
107 }
108
109 /* enable active counters */
4c168eaf 110 for (i = 0; i < NUM_COUNTERS; ++i) {
217d3cfb 111 if (counter_config[i].enabled && msrs->counters[i].addr) {
4c168eaf 112 reset_value[i] = counter_config[i].count;
bbc5986d
RR
113 wrmsrl(msrs->counters[i].addr,
114 -(s64)counter_config[i].count);
3370d358
RR
115 rdmsrl(msrs->controls[i].addr, val);
116 val &= model->reserved;
117 val |= op_x86_get_ctrl(model, &counter_config[i]);
118 wrmsrl(msrs->controls[i].addr, val);
4c168eaf
RR
119 } else {
120 reset_value[i] = 0;
1da177e4
LT
121 }
122 }
123}
124
852402cc
RR
125#ifdef CONFIG_OPROFILE_IBS
126
7939d2bf
RR
127static inline int
128op_amd_handle_ibs(struct pt_regs * const regs,
129 struct op_msrs const * const msrs)
1da177e4 130{
c572ae4e 131 u64 val, ctl;
1acda878 132 struct op_entry entry;
1da177e4 133
fc81be8c 134 if (!has_ibs)
21e70878 135 return 0;
1da177e4 136
7939d2bf 137 if (ibs_config.fetch_enabled) {
c572ae4e
RR
138 rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl);
139 if (ctl & IBS_FETCH_VAL) {
140 rdmsrl(MSR_AMD64_IBSFETCHLINAD, val);
141 oprofile_write_reserve(&entry, regs, val,
14f0ca8e 142 IBS_FETCH_CODE, IBS_FETCH_SIZE);
51563a0e
RR
143 oprofile_add_data64(&entry, val);
144 oprofile_add_data64(&entry, ctl);
c572ae4e 145 rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val);
51563a0e 146 oprofile_add_data64(&entry, val);
14f0ca8e 147 oprofile_write_commit(&entry);
56784f11 148
fd13f6c8 149 /* reenable the IRQ */
c572ae4e
RR
150 ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT_MASK);
151 ctl |= IBS_FETCH_ENABLE;
152 wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl);
56784f11
BK
153 }
154 }
155
7939d2bf 156 if (ibs_config.op_enabled) {
c572ae4e
RR
157 rdmsrl(MSR_AMD64_IBSOPCTL, ctl);
158 if (ctl & IBS_OP_VAL) {
159 rdmsrl(MSR_AMD64_IBSOPRIP, val);
160 oprofile_write_reserve(&entry, regs, val,
14f0ca8e 161 IBS_OP_CODE, IBS_OP_SIZE);
51563a0e 162 oprofile_add_data64(&entry, val);
c572ae4e 163 rdmsrl(MSR_AMD64_IBSOPDATA, val);
51563a0e 164 oprofile_add_data64(&entry, val);
c572ae4e 165 rdmsrl(MSR_AMD64_IBSOPDATA2, val);
51563a0e 166 oprofile_add_data64(&entry, val);
c572ae4e 167 rdmsrl(MSR_AMD64_IBSOPDATA3, val);
51563a0e 168 oprofile_add_data64(&entry, val);
c572ae4e 169 rdmsrl(MSR_AMD64_IBSDCLINAD, val);
51563a0e 170 oprofile_add_data64(&entry, val);
c572ae4e 171 rdmsrl(MSR_AMD64_IBSDCPHYSAD, val);
51563a0e 172 oprofile_add_data64(&entry, val);
14f0ca8e 173 oprofile_write_commit(&entry);
56784f11
BK
174
175 /* reenable the IRQ */
c572ae4e
RR
176 ctl &= ~IBS_OP_VAL & 0xFFFFFFFF;
177 ctl |= IBS_OP_ENABLE;
178 wrmsrl(MSR_AMD64_IBSOPCTL, ctl);
56784f11
BK
179 }
180 }
181
1da177e4
LT
182 return 1;
183}
184
90637595
RR
185static inline void op_amd_start_ibs(void)
186{
c572ae4e 187 u64 val;
90637595 188 if (has_ibs && ibs_config.fetch_enabled) {
c572ae4e
RR
189 val = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
190 val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0;
191 val |= IBS_FETCH_ENABLE;
192 wrmsrl(MSR_AMD64_IBSFETCHCTL, val);
90637595
RR
193 }
194
195 if (has_ibs && ibs_config.op_enabled) {
c572ae4e
RR
196 val = (ibs_config.max_cnt_op >> 4) & 0xFFFF;
197 val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0;
198 val |= IBS_OP_ENABLE;
199 wrmsrl(MSR_AMD64_IBSOPCTL, val);
90637595
RR
200 }
201}
202
203static void op_amd_stop_ibs(void)
204{
c572ae4e 205 if (has_ibs && ibs_config.fetch_enabled)
90637595 206 /* clear max count and enable */
c572ae4e 207 wrmsrl(MSR_AMD64_IBSFETCHCTL, 0);
90637595 208
c572ae4e 209 if (has_ibs && ibs_config.op_enabled)
90637595 210 /* clear max count and enable */
c572ae4e 211 wrmsrl(MSR_AMD64_IBSOPCTL, 0);
90637595
RR
212}
213
214#else
215
216static inline int op_amd_handle_ibs(struct pt_regs * const regs,
21e70878
JSR
217 struct op_msrs const * const msrs)
218{
219 return 0;
220}
90637595
RR
221static inline void op_amd_start_ibs(void) { }
222static inline void op_amd_stop_ibs(void) { }
223
852402cc
RR
224#endif
225
7939d2bf
RR
226static int op_amd_check_ctrs(struct pt_regs * const regs,
227 struct op_msrs const * const msrs)
228{
42399adb 229 u64 val;
7939d2bf
RR
230 int i;
231
4c168eaf
RR
232 for (i = 0 ; i < NUM_COUNTERS; ++i) {
233 if (!reset_value[i])
7939d2bf 234 continue;
42399adb
RR
235 rdmsrl(msrs->counters[i].addr, val);
236 /* bit is clear if overflowed: */
237 if (val & OP_CTR_OVERFLOW)
238 continue;
239 oprofile_add_sample(regs, i);
bbc5986d 240 wrmsrl(msrs->counters[i].addr, -(s64)reset_value[i]);
7939d2bf
RR
241 }
242
243 op_amd_handle_ibs(regs, msrs);
244
245 /* See op_model_ppro.c */
246 return 1;
247}
d4413732 248
6657fe4f 249static void op_amd_start(struct op_msrs const * const msrs)
1da177e4 250{
dea3766c 251 u64 val;
1da177e4 252 int i;
4c168eaf
RR
253 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
254 if (reset_value[i]) {
dea3766c
RR
255 rdmsrl(msrs->controls[i].addr, val);
256 val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
257 wrmsrl(msrs->controls[i].addr, val);
1da177e4
LT
258 }
259 }
852402cc 260
90637595 261 op_amd_start_ibs();
1da177e4
LT
262}
263
6657fe4f 264static void op_amd_stop(struct op_msrs const * const msrs)
1da177e4 265{
dea3766c 266 u64 val;
1da177e4
LT
267 int i;
268
fd13f6c8
RR
269 /*
270 * Subtle: stop on all counters to avoid race with setting our
271 * pm callback
272 */
4c168eaf
RR
273 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
274 if (!reset_value[i])
cb9c448c 275 continue;
dea3766c
RR
276 rdmsrl(msrs->controls[i].addr, val);
277 val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE;
278 wrmsrl(msrs->controls[i].addr, val);
1da177e4 279 }
56784f11 280
90637595 281 op_amd_stop_ibs();
1da177e4
LT
282}
283
6657fe4f 284static void op_amd_shutdown(struct op_msrs const * const msrs)
cb9c448c
DZ
285{
286 int i;
287
4c168eaf 288 for (i = 0 ; i < NUM_COUNTERS ; ++i) {
217d3cfb 289 if (msrs->counters[i].addr)
cb9c448c
DZ
290 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
291 }
4c168eaf 292 for (i = 0 ; i < NUM_CONTROLS ; ++i) {
217d3cfb 293 if (msrs->controls[i].addr)
cb9c448c
DZ
294 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
295 }
296}
1da177e4 297
9fa6812d 298#ifdef CONFIG_OPROFILE_IBS
a4c408a4 299
7d77f2dc
RR
300static u8 ibs_eilvt_off;
301
56784f11
BK
302static inline void apic_init_ibs_nmi_per_cpu(void *arg)
303{
7d77f2dc 304 ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
56784f11
BK
305}
306
307static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
308{
309 setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
310}
311
fe615cbf 312static int init_ibs_nmi(void)
7d77f2dc
RR
313{
314#define IBSCTL_LVTOFFSETVAL (1 << 8)
315#define IBSCTL 0x1cc
316 struct pci_dev *cpu_cfg;
317 int nodes;
318 u32 value = 0;
319
320 /* per CPU setup */
ebb535de 321 on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
7d77f2dc
RR
322
323 nodes = 0;
324 cpu_cfg = NULL;
325 do {
326 cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD,
327 PCI_DEVICE_ID_AMD_10H_NB_MISC,
328 cpu_cfg);
329 if (!cpu_cfg)
330 break;
331 ++nodes;
332 pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
333 | IBSCTL_LVTOFFSETVAL);
334 pci_read_config_dword(cpu_cfg, IBSCTL, &value);
335 if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
83bd9243 336 pci_dev_put(cpu_cfg);
7d77f2dc
RR
337 printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
338 "IBSCTL = 0x%08x", value);
339 return 1;
340 }
341 } while (1);
342
343 if (!nodes) {
344 printk(KERN_DEBUG "No CPU node configured for IBS");
345 return 1;
346 }
347
348#ifdef CONFIG_NUMA
349 /* Sanity check */
350 /* Works only for 64bit with proper numa implementation. */
351 if (nodes != num_possible_nodes()) {
352 printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, "
353 "found: %d, expected %d",
354 nodes, num_possible_nodes());
355 return 1;
356 }
357#endif
358 return 0;
359}
360
fe615cbf
RR
361/* uninitialize the APIC for the IBS interrupts if needed */
362static void clear_ibs_nmi(void)
363{
fc81be8c 364 if (has_ibs)
fe615cbf
RR
365 on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
366}
367
fd13f6c8 368/* initialize the APIC for the IBS interrupts if available */
fe615cbf 369static void ibs_init(void)
56784f11 370{
fc81be8c 371 has_ibs = boot_cpu_has(X86_FEATURE_IBS);
56784f11 372
fc81be8c 373 if (!has_ibs)
56784f11
BK
374 return;
375
fe615cbf 376 if (init_ibs_nmi()) {
fc81be8c 377 has_ibs = 0;
852402cc
RR
378 return;
379 }
380
381 printk(KERN_INFO "oprofile: AMD IBS detected\n");
56784f11
BK
382}
383
fe615cbf 384static void ibs_exit(void)
56784f11 385{
fc81be8c 386 if (!has_ibs)
fe615cbf
RR
387 return;
388
389 clear_ibs_nmi();
56784f11
BK
390}
391
25ad2913 392static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
270d3e1a 393
25ad2913 394static int setup_ibs_files(struct super_block *sb, struct dentry *root)
56784f11 395{
56784f11 396 struct dentry *dir;
270d3e1a
RR
397 int ret = 0;
398
399 /* architecture specific files */
400 if (create_arch_files)
401 ret = create_arch_files(sb, root);
402
403 if (ret)
404 return ret;
56784f11 405
fc81be8c 406 if (!has_ibs)
270d3e1a
RR
407 return ret;
408
409 /* model specific files */
56784f11
BK
410
411 /* setup some reasonable defaults */
412 ibs_config.max_cnt_fetch = 250000;
413 ibs_config.fetch_enabled = 0;
414 ibs_config.max_cnt_op = 250000;
415 ibs_config.op_enabled = 0;
416 ibs_config.dispatched_ops = 1;
2d55a478
RR
417
418 dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
56784f11 419 oprofilefs_create_ulong(sb, dir, "enable",
2d55a478 420 &ibs_config.fetch_enabled);
56784f11 421 oprofilefs_create_ulong(sb, dir, "max_count",
2d55a478
RR
422 &ibs_config.max_cnt_fetch);
423 oprofilefs_create_ulong(sb, dir, "rand_enable",
424 &ibs_config.rand_en);
425
ccd755c2 426 dir = oprofilefs_mkdir(sb, root, "ibs_op");
56784f11 427 oprofilefs_create_ulong(sb, dir, "enable",
2d55a478 428 &ibs_config.op_enabled);
56784f11 429 oprofilefs_create_ulong(sb, dir, "max_count",
2d55a478 430 &ibs_config.max_cnt_op);
56784f11 431 oprofilefs_create_ulong(sb, dir, "dispatched_ops",
2d55a478 432 &ibs_config.dispatched_ops);
fc2bd734
RR
433
434 return 0;
56784f11
BK
435}
436
adf5ec0b
RR
437static int op_amd_init(struct oprofile_operations *ops)
438{
fe615cbf 439 ibs_init();
270d3e1a
RR
440 create_arch_files = ops->create_files;
441 ops->create_files = setup_ibs_files;
adf5ec0b
RR
442 return 0;
443}
444
445static void op_amd_exit(void)
446{
fe615cbf 447 ibs_exit();
adf5ec0b
RR
448}
449
9fa6812d
RR
450#else
451
452/* no IBS support */
453
454static int op_amd_init(struct oprofile_operations *ops)
455{
456 return 0;
457}
458
459static void op_amd_exit(void) {}
460
461#endif /* CONFIG_OPROFILE_IBS */
a4c408a4 462
6657fe4f 463struct op_x86_model_spec const op_amd_spec = {
c92960fc
RR
464 .num_counters = NUM_COUNTERS,
465 .num_controls = NUM_CONTROLS,
3370d358
RR
466 .reserved = MSR_AMD_EVENTSEL_RESERVED,
467 .event_mask = OP_EVENT_MASK,
468 .init = op_amd_init,
469 .exit = op_amd_exit,
c92960fc
RR
470 .fill_in_addresses = &op_amd_fill_in_addresses,
471 .setup_ctrs = &op_amd_setup_ctrs,
472 .check_ctrs = &op_amd_check_ctrs,
473 .start = &op_amd_start,
474 .stop = &op_amd_stop,
3370d358 475 .shutdown = &op_amd_shutdown,
1da177e4 476};