diff options
author | Robert Richter <robert.richter@amd.com> | 2011-09-21 05:30:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-10-10 00:57:16 -0400 |
commit | b716916679e72054d436afadce2f94dcad71cfad (patch) | |
tree | 86e90ea74a78fb42276868c3ecd1af714d9e5b20 /arch/x86/oprofile | |
parent | ee5789dbcc800ba7d641443e53f60d53977f9747 (diff) |
perf, x86: Implement IBS initialization
This patch implements IBS feature detection and initialzation. The
code is shared between perf and oprofile. If IBS is available on the
system for perf, a pmu is setup.
Signed-off-by: Robert Richter <robert.richter@amd.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1316597423-25723-3-git-send-email-robert.richter@amd.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/oprofile')
-rw-r--r-- | arch/x86/oprofile/nmi_int.c | 2 | ||||
-rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 197 | ||||
-rw-r--r-- | arch/x86/oprofile/op_x86_model.h | 1 |
3 files changed, 0 insertions, 200 deletions
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index adf8fb31aa7b..c04dc145a4b7 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c | |||
@@ -385,8 +385,6 @@ static void nmi_cpu_shutdown(void *dummy) | |||
385 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); | 385 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); |
386 | apic_write(APIC_LVTERR, v); | 386 | apic_write(APIC_LVTERR, v); |
387 | nmi_cpu_restore_registers(msrs); | 387 | nmi_cpu_restore_registers(msrs); |
388 | if (model->cpu_down) | ||
389 | model->cpu_down(); | ||
390 | } | 388 | } |
391 | 389 | ||
392 | static void nmi_cpu_up(void *dummy) | 390 | static void nmi_cpu_up(void *dummy) |
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index e947e5cb2e61..303f08637826 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -74,27 +74,6 @@ static struct ibs_state ibs_state; | |||
74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) | 74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) |
75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) | 75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) |
76 | 76 | ||
77 | static u32 get_ibs_caps(void) | ||
78 | { | ||
79 | u32 ibs_caps; | ||
80 | unsigned int max_level; | ||
81 | |||
82 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
83 | return 0; | ||
84 | |||
85 | /* check IBS cpuid feature flags */ | ||
86 | max_level = cpuid_eax(0x80000000); | ||
87 | if (max_level < IBS_CPUID_FEATURES) | ||
88 | return IBS_CAPS_DEFAULT; | ||
89 | |||
90 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
91 | if (!(ibs_caps & IBS_CAPS_AVAIL)) | ||
92 | /* cpuid flags not valid */ | ||
93 | return IBS_CAPS_DEFAULT; | ||
94 | |||
95 | return ibs_caps; | ||
96 | } | ||
97 | |||
98 | /* | 77 | /* |
99 | * 16-bit Linear Feedback Shift Register (LFSR) | 78 | * 16-bit Linear Feedback Shift Register (LFSR) |
100 | * | 79 | * |
@@ -285,81 +264,6 @@ static void op_amd_stop_ibs(void) | |||
285 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 264 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
286 | } | 265 | } |
287 | 266 | ||
288 | static inline int get_eilvt(int offset) | ||
289 | { | ||
290 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
291 | } | ||
292 | |||
293 | static inline int put_eilvt(int offset) | ||
294 | { | ||
295 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
296 | } | ||
297 | |||
298 | static inline int ibs_eilvt_valid(void) | ||
299 | { | ||
300 | int offset; | ||
301 | u64 val; | ||
302 | int valid = 0; | ||
303 | |||
304 | preempt_disable(); | ||
305 | |||
306 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
307 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
308 | |||
309 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
310 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
311 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
312 | goto out; | ||
313 | } | ||
314 | |||
315 | if (!get_eilvt(offset)) { | ||
316 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
317 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | valid = 1; | ||
322 | out: | ||
323 | preempt_enable(); | ||
324 | |||
325 | return valid; | ||
326 | } | ||
327 | |||
328 | static inline int get_ibs_offset(void) | ||
329 | { | ||
330 | u64 val; | ||
331 | |||
332 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
333 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
334 | return -EINVAL; | ||
335 | |||
336 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
337 | } | ||
338 | |||
339 | static void setup_APIC_ibs(void) | ||
340 | { | ||
341 | int offset; | ||
342 | |||
343 | offset = get_ibs_offset(); | ||
344 | if (offset < 0) | ||
345 | goto failed; | ||
346 | |||
347 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
348 | return; | ||
349 | failed: | ||
350 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
351 | smp_processor_id()); | ||
352 | } | ||
353 | |||
354 | static void clear_APIC_ibs(void) | ||
355 | { | ||
356 | int offset; | ||
357 | |||
358 | offset = get_ibs_offset(); | ||
359 | if (offset >= 0) | ||
360 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
361 | } | ||
362 | |||
363 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 267 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
364 | 268 | ||
365 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 269 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
@@ -473,15 +377,6 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
473 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | 377 | val |= op_x86_get_ctrl(model, &counter_config[virt]); |
474 | wrmsrl(msrs->controls[i].addr, val); | 378 | wrmsrl(msrs->controls[i].addr, val); |
475 | } | 379 | } |
476 | |||
477 | if (ibs_caps) | ||
478 | setup_APIC_ibs(); | ||
479 | } | ||
480 | |||
481 | static void op_amd_cpu_shutdown(void) | ||
482 | { | ||
483 | if (ibs_caps) | ||
484 | clear_APIC_ibs(); | ||
485 | } | 380 | } |
486 | 381 | ||
487 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 382 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
@@ -544,86 +439,6 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
544 | op_amd_stop_ibs(); | 439 | op_amd_stop_ibs(); |
545 | } | 440 | } |
546 | 441 | ||
547 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
548 | { | ||
549 | struct pci_dev *cpu_cfg; | ||
550 | int nodes; | ||
551 | u32 value = 0; | ||
552 | |||
553 | nodes = 0; | ||
554 | cpu_cfg = NULL; | ||
555 | do { | ||
556 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
557 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
558 | cpu_cfg); | ||
559 | if (!cpu_cfg) | ||
560 | break; | ||
561 | ++nodes; | ||
562 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
563 | | IBSCTL_LVT_OFFSET_VALID); | ||
564 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
565 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
566 | pci_dev_put(cpu_cfg); | ||
567 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
568 | "IBSCTL = 0x%08x\n", value); | ||
569 | return -EINVAL; | ||
570 | } | ||
571 | } while (1); | ||
572 | |||
573 | if (!nodes) { | ||
574 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
575 | return -ENODEV; | ||
576 | } | ||
577 | |||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * This runs only on the current cpu. We try to find an LVT offset and | ||
583 | * setup the local APIC. For this we must disable preemption. On | ||
584 | * success we initialize all nodes with this offset. This updates then | ||
585 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
586 | * the IBS interrupt vector is called from op_amd_setup_ctrs()/op_- | ||
587 | * amd_cpu_shutdown() using the new offset. | ||
588 | */ | ||
589 | static int force_ibs_eilvt_setup(void) | ||
590 | { | ||
591 | int offset; | ||
592 | int ret; | ||
593 | |||
594 | preempt_disable(); | ||
595 | /* find the next free available EILVT entry, skip offset 0 */ | ||
596 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
597 | if (get_eilvt(offset)) | ||
598 | break; | ||
599 | } | ||
600 | preempt_enable(); | ||
601 | |||
602 | if (offset == APIC_EILVT_NR_MAX) { | ||
603 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
604 | return -EBUSY; | ||
605 | } | ||
606 | |||
607 | ret = setup_ibs_ctl(offset); | ||
608 | if (ret) | ||
609 | goto out; | ||
610 | |||
611 | if (!ibs_eilvt_valid()) { | ||
612 | ret = -EFAULT; | ||
613 | goto out; | ||
614 | } | ||
615 | |||
616 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
617 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
618 | |||
619 | return 0; | ||
620 | out: | ||
621 | preempt_disable(); | ||
622 | put_eilvt(offset); | ||
623 | preempt_enable(); | ||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | /* | 442 | /* |
628 | * check and reserve APIC extended interrupt LVT offset for IBS if | 443 | * check and reserve APIC extended interrupt LVT offset for IBS if |
629 | * available | 444 | * available |
@@ -636,17 +451,6 @@ static void init_ibs(void) | |||
636 | if (!ibs_caps) | 451 | if (!ibs_caps) |
637 | return; | 452 | return; |
638 | 453 | ||
639 | if (ibs_eilvt_valid()) | ||
640 | goto out; | ||
641 | |||
642 | if (!force_ibs_eilvt_setup()) | ||
643 | goto out; | ||
644 | |||
645 | /* Failed to setup ibs */ | ||
646 | ibs_caps = 0; | ||
647 | return; | ||
648 | |||
649 | out: | ||
650 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); | 454 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); |
651 | } | 455 | } |
652 | 456 | ||
@@ -729,7 +533,6 @@ struct op_x86_model_spec op_amd_spec = { | |||
729 | .init = op_amd_init, | 533 | .init = op_amd_init, |
730 | .fill_in_addresses = &op_amd_fill_in_addresses, | 534 | .fill_in_addresses = &op_amd_fill_in_addresses, |
731 | .setup_ctrs = &op_amd_setup_ctrs, | 535 | .setup_ctrs = &op_amd_setup_ctrs, |
732 | .cpu_down = &op_amd_cpu_shutdown, | ||
733 | .check_ctrs = &op_amd_check_ctrs, | 536 | .check_ctrs = &op_amd_check_ctrs, |
734 | .start = &op_amd_start, | 537 | .start = &op_amd_start, |
735 | .stop = &op_amd_stop, | 538 | .stop = &op_amd_stop, |
diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index 89017fa1fd63..71e8a67337e2 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h | |||
@@ -43,7 +43,6 @@ struct op_x86_model_spec { | |||
43 | int (*fill_in_addresses)(struct op_msrs * const msrs); | 43 | int (*fill_in_addresses)(struct op_msrs * const msrs); |
44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, | 44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, |
45 | struct op_msrs const * const msrs); | 45 | struct op_msrs const * const msrs); |
46 | void (*cpu_down)(void); | ||
47 | int (*check_ctrs)(struct pt_regs * const regs, | 46 | int (*check_ctrs)(struct pt_regs * const regs, |
48 | struct op_msrs const * const msrs); | 47 | struct op_msrs const * const msrs); |
49 | void (*start)(struct op_msrs const * const msrs); | 48 | void (*start)(struct op_msrs const * const msrs); |