diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /arch/x86/oprofile/op_model_amd.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'arch/x86/oprofile/op_model_amd.c')
-rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 234 |
1 files changed, 231 insertions, 3 deletions
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index b2b94438ff0..9cbb710dc94 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include "op_x86_model.h" | 29 | #include "op_x86_model.h" |
30 | #include "op_counter.h" | 30 | #include "op_counter.h" |
31 | 31 | ||
32 | #define NUM_COUNTERS 4 | ||
33 | #define NUM_COUNTERS_F15H 6 | ||
32 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 34 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
33 | #define NUM_VIRT_COUNTERS 32 | 35 | #define NUM_VIRT_COUNTERS 32 |
34 | #else | 36 | #else |
@@ -68,12 +70,62 @@ static struct ibs_config ibs_config; | |||
68 | static struct ibs_state ibs_state; | 70 | static struct ibs_state ibs_state; |
69 | 71 | ||
70 | /* | 72 | /* |
73 | * IBS cpuid feature detection | ||
74 | */ | ||
75 | |||
76 | #define IBS_CPUID_FEATURES 0x8000001b | ||
77 | |||
78 | /* | ||
79 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | ||
80 | * bit 0 is used to indicate the existence of IBS. | ||
81 | */ | ||
82 | #define IBS_CAPS_AVAIL (1U<<0) | ||
83 | #define IBS_CAPS_FETCHSAM (1U<<1) | ||
84 | #define IBS_CAPS_OPSAM (1U<<2) | ||
85 | #define IBS_CAPS_RDWROPCNT (1U<<3) | ||
86 | #define IBS_CAPS_OPCNT (1U<<4) | ||
87 | #define IBS_CAPS_BRNTRGT (1U<<5) | ||
88 | #define IBS_CAPS_OPCNTEXT (1U<<6) | ||
89 | |||
90 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ | ||
91 | | IBS_CAPS_FETCHSAM \ | ||
92 | | IBS_CAPS_OPSAM) | ||
93 | |||
94 | /* | ||
95 | * IBS APIC setup | ||
96 | */ | ||
97 | #define IBSCTL 0x1cc | ||
98 | #define IBSCTL_LVT_OFFSET_VALID (1ULL<<8) | ||
99 | #define IBSCTL_LVT_OFFSET_MASK 0x0F | ||
100 | |||
101 | /* | ||
71 | * IBS randomization macros | 102 | * IBS randomization macros |
72 | */ | 103 | */ |
73 | #define IBS_RANDOM_BITS 12 | 104 | #define IBS_RANDOM_BITS 12 |
74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) | 105 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) |
75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) | 106 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) |
76 | 107 | ||
108 | static u32 get_ibs_caps(void) | ||
109 | { | ||
110 | u32 ibs_caps; | ||
111 | unsigned int max_level; | ||
112 | |||
113 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
114 | return 0; | ||
115 | |||
116 | /* check IBS cpuid feature flags */ | ||
117 | max_level = cpuid_eax(0x80000000); | ||
118 | if (max_level < IBS_CPUID_FEATURES) | ||
119 | return IBS_CAPS_DEFAULT; | ||
120 | |||
121 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
122 | if (!(ibs_caps & IBS_CAPS_AVAIL)) | ||
123 | /* cpuid flags not valid */ | ||
124 | return IBS_CAPS_DEFAULT; | ||
125 | |||
126 | return ibs_caps; | ||
127 | } | ||
128 | |||
77 | /* | 129 | /* |
78 | * 16-bit Linear Feedback Shift Register (LFSR) | 130 | * 16-bit Linear Feedback Shift Register (LFSR) |
79 | * | 131 | * |
@@ -264,6 +316,81 @@ static void op_amd_stop_ibs(void) | |||
264 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 316 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
265 | } | 317 | } |
266 | 318 | ||
319 | static inline int get_eilvt(int offset) | ||
320 | { | ||
321 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
322 | } | ||
323 | |||
324 | static inline int put_eilvt(int offset) | ||
325 | { | ||
326 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
327 | } | ||
328 | |||
329 | static inline int ibs_eilvt_valid(void) | ||
330 | { | ||
331 | int offset; | ||
332 | u64 val; | ||
333 | int valid = 0; | ||
334 | |||
335 | preempt_disable(); | ||
336 | |||
337 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
338 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
339 | |||
340 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
341 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
342 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | if (!get_eilvt(offset)) { | ||
347 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
348 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
349 | goto out; | ||
350 | } | ||
351 | |||
352 | valid = 1; | ||
353 | out: | ||
354 | preempt_enable(); | ||
355 | |||
356 | return valid; | ||
357 | } | ||
358 | |||
359 | static inline int get_ibs_offset(void) | ||
360 | { | ||
361 | u64 val; | ||
362 | |||
363 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
364 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
365 | return -EINVAL; | ||
366 | |||
367 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
368 | } | ||
369 | |||
370 | static void setup_APIC_ibs(void) | ||
371 | { | ||
372 | int offset; | ||
373 | |||
374 | offset = get_ibs_offset(); | ||
375 | if (offset < 0) | ||
376 | goto failed; | ||
377 | |||
378 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
379 | return; | ||
380 | failed: | ||
381 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
382 | smp_processor_id()); | ||
383 | } | ||
384 | |||
385 | static void clear_APIC_ibs(void) | ||
386 | { | ||
387 | int offset; | ||
388 | |||
389 | offset = get_ibs_offset(); | ||
390 | if (offset >= 0) | ||
391 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
392 | } | ||
393 | |||
267 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 394 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
268 | 395 | ||
269 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 396 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
@@ -312,7 +439,7 @@ static int op_amd_fill_in_addresses(struct op_msrs * const msrs) | |||
312 | goto fail; | 439 | goto fail; |
313 | } | 440 | } |
314 | /* both registers must be reserved */ | 441 | /* both registers must be reserved */ |
315 | if (num_counters == AMD64_NUM_COUNTERS_CORE) { | 442 | if (num_counters == NUM_COUNTERS_F15H) { |
316 | msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); | 443 | msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); |
317 | msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); | 444 | msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); |
318 | } else { | 445 | } else { |
@@ -377,6 +504,15 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
377 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | 504 | val |= op_x86_get_ctrl(model, &counter_config[virt]); |
378 | wrmsrl(msrs->controls[i].addr, val); | 505 | wrmsrl(msrs->controls[i].addr, val); |
379 | } | 506 | } |
507 | |||
508 | if (ibs_caps) | ||
509 | setup_APIC_ibs(); | ||
510 | } | ||
511 | |||
512 | static void op_amd_cpu_shutdown(void) | ||
513 | { | ||
514 | if (ibs_caps) | ||
515 | clear_APIC_ibs(); | ||
380 | } | 516 | } |
381 | 517 | ||
382 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 518 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
@@ -439,6 +575,86 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
439 | op_amd_stop_ibs(); | 575 | op_amd_stop_ibs(); |
440 | } | 576 | } |
441 | 577 | ||
578 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
579 | { | ||
580 | struct pci_dev *cpu_cfg; | ||
581 | int nodes; | ||
582 | u32 value = 0; | ||
583 | |||
584 | nodes = 0; | ||
585 | cpu_cfg = NULL; | ||
586 | do { | ||
587 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
588 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
589 | cpu_cfg); | ||
590 | if (!cpu_cfg) | ||
591 | break; | ||
592 | ++nodes; | ||
593 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
594 | | IBSCTL_LVT_OFFSET_VALID); | ||
595 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
596 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
597 | pci_dev_put(cpu_cfg); | ||
598 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
599 | "IBSCTL = 0x%08x\n", value); | ||
600 | return -EINVAL; | ||
601 | } | ||
602 | } while (1); | ||
603 | |||
604 | if (!nodes) { | ||
605 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
606 | return -ENODEV; | ||
607 | } | ||
608 | |||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * This runs only on the current cpu. We try to find an LVT offset and | ||
614 | * setup the local APIC. For this we must disable preemption. On | ||
615 | * success we initialize all nodes with this offset. This updates then | ||
616 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
617 | * the IBS interrupt vector is called from op_amd_setup_ctrs()/op_- | ||
618 | * amd_cpu_shutdown() using the new offset. | ||
619 | */ | ||
620 | static int force_ibs_eilvt_setup(void) | ||
621 | { | ||
622 | int offset; | ||
623 | int ret; | ||
624 | |||
625 | preempt_disable(); | ||
626 | /* find the next free available EILVT entry, skip offset 0 */ | ||
627 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
628 | if (get_eilvt(offset)) | ||
629 | break; | ||
630 | } | ||
631 | preempt_enable(); | ||
632 | |||
633 | if (offset == APIC_EILVT_NR_MAX) { | ||
634 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
635 | return -EBUSY; | ||
636 | } | ||
637 | |||
638 | ret = setup_ibs_ctl(offset); | ||
639 | if (ret) | ||
640 | goto out; | ||
641 | |||
642 | if (!ibs_eilvt_valid()) { | ||
643 | ret = -EFAULT; | ||
644 | goto out; | ||
645 | } | ||
646 | |||
647 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
648 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
649 | |||
650 | return 0; | ||
651 | out: | ||
652 | preempt_disable(); | ||
653 | put_eilvt(offset); | ||
654 | preempt_enable(); | ||
655 | return ret; | ||
656 | } | ||
657 | |||
442 | /* | 658 | /* |
443 | * check and reserve APIC extended interrupt LVT offset for IBS if | 659 | * check and reserve APIC extended interrupt LVT offset for IBS if |
444 | * available | 660 | * available |
@@ -451,6 +667,17 @@ static void init_ibs(void) | |||
451 | if (!ibs_caps) | 667 | if (!ibs_caps) |
452 | return; | 668 | return; |
453 | 669 | ||
670 | if (ibs_eilvt_valid()) | ||
671 | goto out; | ||
672 | |||
673 | if (!force_ibs_eilvt_setup()) | ||
674 | goto out; | ||
675 | |||
676 | /* Failed to setup ibs */ | ||
677 | ibs_caps = 0; | ||
678 | return; | ||
679 | |||
680 | out: | ||
454 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); | 681 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); |
455 | } | 682 | } |
456 | 683 | ||
@@ -514,9 +741,9 @@ static int op_amd_init(struct oprofile_operations *ops) | |||
514 | ops->create_files = setup_ibs_files; | 741 | ops->create_files = setup_ibs_files; |
515 | 742 | ||
516 | if (boot_cpu_data.x86 == 0x15) { | 743 | if (boot_cpu_data.x86 == 0x15) { |
517 | num_counters = AMD64_NUM_COUNTERS_CORE; | 744 | num_counters = NUM_COUNTERS_F15H; |
518 | } else { | 745 | } else { |
519 | num_counters = AMD64_NUM_COUNTERS; | 746 | num_counters = NUM_COUNTERS; |
520 | } | 747 | } |
521 | 748 | ||
522 | op_amd_spec.num_counters = num_counters; | 749 | op_amd_spec.num_counters = num_counters; |
@@ -533,6 +760,7 @@ struct op_x86_model_spec op_amd_spec = { | |||
533 | .init = op_amd_init, | 760 | .init = op_amd_init, |
534 | .fill_in_addresses = &op_amd_fill_in_addresses, | 761 | .fill_in_addresses = &op_amd_fill_in_addresses, |
535 | .setup_ctrs = &op_amd_setup_ctrs, | 762 | .setup_ctrs = &op_amd_setup_ctrs, |
763 | .cpu_down = &op_amd_cpu_shutdown, | ||
536 | .check_ctrs = &op_amd_check_ctrs, | 764 | .check_ctrs = &op_amd_check_ctrs, |
537 | .start = &op_amd_start, | 765 | .start = &op_amd_start, |
538 | .stop = &op_amd_stop, | 766 | .stop = &op_amd_stop, |