diff options
author | Robert Richter <robert.richter@amd.com> | 2010-10-25 10:58:34 -0400 |
---|---|---|
committer | Robert Richter <robert.richter@amd.com> | 2010-10-25 10:58:34 -0400 |
commit | 4cafc4b8d7219b70e15f22e4a51b3ce847810caf (patch) | |
tree | 8051ea3f36f0682d08f47df8e35e14ca7eb7a5d7 /arch/x86/oprofile | |
parent | b47fad3bfb5940cc3e28a1c69716f6dc44e4b7e6 (diff) | |
parent | dbd1e66e04558a582e673bc4a9cd933ce0228d93 (diff) |
Merge branch 'oprofile/core' into oprofile/x86
Conflicts:
arch/x86/oprofile/op_model_amd.c
Signed-off-by: Robert Richter <robert.richter@amd.com>
Diffstat (limited to 'arch/x86/oprofile')
-rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 132 |
1 files changed, 117 insertions, 15 deletions
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index 65f0a1eb6b86..8d17db266bbf 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -71,7 +71,7 @@ static struct ibs_state ibs_state; | |||
71 | * IBS cpuid feature detection | 71 | * IBS cpuid feature detection |
72 | */ | 72 | */ |
73 | 73 | ||
74 | #define IBS_CPUID_FEATURES 0x8000001b | 74 | #define IBS_CPUID_FEATURES 0x8000001b |
75 | 75 | ||
76 | /* | 76 | /* |
77 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | 77 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but |
@@ -314,6 +314,74 @@ static void op_amd_stop_ibs(void) | |||
314 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 314 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
315 | } | 315 | } |
316 | 316 | ||
317 | static inline int eilvt_is_available(int offset) | ||
318 | { | ||
319 | /* check if we may assign a vector */ | ||
320 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
321 | } | ||
322 | |||
323 | static inline int ibs_eilvt_valid(void) | ||
324 | { | ||
325 | u64 val; | ||
326 | int offset; | ||
327 | |||
328 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
329 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
330 | pr_err(FW_BUG "cpu %d, invalid IBS " | ||
331 | "interrupt offset %d (MSR%08X=0x%016llx)", | ||
332 | smp_processor_id(), offset, | ||
333 | MSR_AMD64_IBSCTL, val); | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
338 | |||
339 | if (eilvt_is_available(offset)) | ||
340 | return !0; | ||
341 | |||
342 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d " | ||
343 | "not available (MSR%08X=0x%016llx)", | ||
344 | smp_processor_id(), offset, | ||
345 | MSR_AMD64_IBSCTL, val); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static inline int get_ibs_offset(void) | ||
351 | { | ||
352 | u64 val; | ||
353 | |||
354 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
355 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
356 | return -EINVAL; | ||
357 | |||
358 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
359 | } | ||
360 | |||
361 | static void setup_APIC_ibs(void) | ||
362 | { | ||
363 | int offset; | ||
364 | |||
365 | offset = get_ibs_offset(); | ||
366 | if (offset < 0) | ||
367 | goto failed; | ||
368 | |||
369 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
370 | return; | ||
371 | failed: | ||
372 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
373 | smp_processor_id()); | ||
374 | } | ||
375 | |||
376 | static void clear_APIC_ibs(void) | ||
377 | { | ||
378 | int offset; | ||
379 | |||
380 | offset = get_ibs_offset(); | ||
381 | if (offset >= 0) | ||
382 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
383 | } | ||
384 | |||
317 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 385 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
318 | 386 | ||
319 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 387 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
@@ -424,13 +492,13 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
424 | } | 492 | } |
425 | 493 | ||
426 | if (ibs_caps) | 494 | if (ibs_caps) |
427 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); | 495 | setup_APIC_ibs(); |
428 | } | 496 | } |
429 | 497 | ||
430 | static void op_amd_cpu_shutdown(void) | 498 | static void op_amd_cpu_shutdown(void) |
431 | { | 499 | { |
432 | if (ibs_caps) | 500 | if (ibs_caps) |
433 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | 501 | clear_APIC_ibs(); |
434 | } | 502 | } |
435 | 503 | ||
436 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 504 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
@@ -493,16 +561,11 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
493 | op_amd_stop_ibs(); | 561 | op_amd_stop_ibs(); |
494 | } | 562 | } |
495 | 563 | ||
496 | static int __init_ibs_nmi(void) | 564 | static int setup_ibs_ctl(int ibs_eilvt_off) |
497 | { | 565 | { |
498 | #define IBSCTL_LVTOFFSETVAL (1 << 8) | ||
499 | #define IBSCTL 0x1cc | ||
500 | struct pci_dev *cpu_cfg; | 566 | struct pci_dev *cpu_cfg; |
501 | int nodes; | 567 | int nodes; |
502 | u32 value = 0; | 568 | u32 value = 0; |
503 | u8 ibs_eilvt_off; | ||
504 | |||
505 | ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | ||
506 | 569 | ||
507 | nodes = 0; | 570 | nodes = 0; |
508 | cpu_cfg = NULL; | 571 | cpu_cfg = NULL; |
@@ -514,21 +577,60 @@ static int __init_ibs_nmi(void) | |||
514 | break; | 577 | break; |
515 | ++nodes; | 578 | ++nodes; |
516 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | 579 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off |
517 | | IBSCTL_LVTOFFSETVAL); | 580 | | IBSCTL_LVT_OFFSET_VALID); |
518 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | 581 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); |
519 | if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | 582 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { |
520 | pci_dev_put(cpu_cfg); | 583 | pci_dev_put(cpu_cfg); |
521 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | 584 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " |
522 | "IBSCTL = 0x%08x", value); | 585 | "IBSCTL = 0x%08x\n", value); |
523 | return 1; | 586 | return -EINVAL; |
524 | } | 587 | } |
525 | } while (1); | 588 | } while (1); |
526 | 589 | ||
527 | if (!nodes) { | 590 | if (!nodes) { |
528 | printk(KERN_DEBUG "No CPU node configured for IBS"); | 591 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); |
529 | return 1; | 592 | return -ENODEV; |
593 | } | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static int force_ibs_eilvt_setup(void) | ||
599 | { | ||
600 | int i; | ||
601 | int ret; | ||
602 | |||
603 | /* find the next free available EILVT entry */ | ||
604 | for (i = 1; i < 4; i++) { | ||
605 | if (!eilvt_is_available(i)) | ||
606 | continue; | ||
607 | ret = setup_ibs_ctl(i); | ||
608 | if (ret) | ||
609 | return ret; | ||
610 | return 0; | ||
530 | } | 611 | } |
531 | 612 | ||
613 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
614 | |||
615 | return -EBUSY; | ||
616 | } | ||
617 | |||
618 | static int __init_ibs_nmi(void) | ||
619 | { | ||
620 | int ret; | ||
621 | |||
622 | if (ibs_eilvt_valid()) | ||
623 | return 0; | ||
624 | |||
625 | ret = force_ibs_eilvt_setup(); | ||
626 | if (ret) | ||
627 | return ret; | ||
628 | |||
629 | if (!ibs_eilvt_valid()) | ||
630 | return -EFAULT; | ||
631 | |||
632 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
633 | |||
532 | return 0; | 634 | return 0; |
533 | } | 635 | } |
534 | 636 | ||