diff options
| -rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 95 |
1 files changed, 54 insertions, 41 deletions
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index c3b8e24f2b16..9fd8a567fe1e 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
| @@ -316,16 +316,23 @@ static void op_amd_stop_ibs(void) | |||
| 316 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 316 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | static inline int eilvt_is_available(int offset) | 319 | static inline int get_eilvt(int offset) |
| 320 | { | 320 | { |
| 321 | /* check if we may assign a vector */ | ||
| 322 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | 321 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); |
| 323 | } | 322 | } |
| 324 | 323 | ||
| 324 | static inline int put_eilvt(int offset) | ||
| 325 | { | ||
| 326 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
| 327 | } | ||
| 328 | |||
| 325 | static inline int ibs_eilvt_valid(void) | 329 | static inline int ibs_eilvt_valid(void) |
| 326 | { | 330 | { |
| 327 | int offset; | 331 | int offset; |
| 328 | u64 val; | 332 | u64 val; |
| 333 | int valid = 0; | ||
| 334 | |||
| 335 | preempt_disable(); | ||
| 329 | 336 | ||
| 330 | rdmsrl(MSR_AMD64_IBSCTL, val); | 337 | rdmsrl(MSR_AMD64_IBSCTL, val); |
| 331 | offset = val & IBSCTL_LVT_OFFSET_MASK; | 338 | offset = val & IBSCTL_LVT_OFFSET_MASK; |
| @@ -333,16 +340,20 @@ static inline int ibs_eilvt_valid(void) | |||
| 333 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | 340 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { |
| 334 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | 341 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", |
| 335 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | 342 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); |
| 336 | return 0; | 343 | goto out; |
| 337 | } | 344 | } |
| 338 | 345 | ||
| 339 | if (!eilvt_is_available(offset)) { | 346 | if (!get_eilvt(offset)) { |
| 340 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | 347 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", |
| 341 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | 348 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); |
| 342 | return 0; | 349 | goto out; |
| 343 | } | 350 | } |
| 344 | 351 | ||
| 345 | return 1; | 352 | valid = 1; |
| 353 | out: | ||
| 354 | preempt_enable(); | ||
| 355 | |||
| 356 | return valid; | ||
| 346 | } | 357 | } |
| 347 | 358 | ||
| 348 | static inline int get_ibs_offset(void) | 359 | static inline int get_ibs_offset(void) |
| @@ -600,67 +611,69 @@ static int setup_ibs_ctl(int ibs_eilvt_off) | |||
| 600 | 611 | ||
| 601 | static int force_ibs_eilvt_setup(void) | 612 | static int force_ibs_eilvt_setup(void) |
| 602 | { | 613 | { |
| 603 | int i; | 614 | int offset; |
| 604 | int ret; | 615 | int ret; |
| 605 | 616 | ||
| 606 | /* find the next free available EILVT entry */ | 617 | /* |
| 607 | for (i = 1; i < 4; i++) { | 618 | * find the next free available EILVT entry, skip offset 0, |
| 608 | if (!eilvt_is_available(i)) | 619 | * pin search to this cpu |
| 609 | continue; | 620 | */ |
| 610 | ret = setup_ibs_ctl(i); | 621 | preempt_disable(); |
| 611 | if (ret) | 622 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { |
| 612 | return ret; | 623 | if (get_eilvt(offset)) |
| 613 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", i); | 624 | break; |
| 614 | return 0; | ||
| 615 | } | 625 | } |
| 626 | preempt_enable(); | ||
| 616 | 627 | ||
| 617 | printk(KERN_DEBUG "No EILVT entry available\n"); | 628 | if (offset == APIC_EILVT_NR_MAX) { |
| 618 | 629 | printk(KERN_DEBUG "No EILVT entry available\n"); | |
| 619 | return -EBUSY; | 630 | return -EBUSY; |
| 620 | } | 631 | } |
| 621 | |||
| 622 | static int __init_ibs_nmi(void) | ||
| 623 | { | ||
| 624 | int ret; | ||
| 625 | |||
| 626 | if (ibs_eilvt_valid()) | ||
| 627 | return 0; | ||
| 628 | 632 | ||
| 629 | ret = force_ibs_eilvt_setup(); | 633 | ret = setup_ibs_ctl(offset); |
| 630 | if (ret) | 634 | if (ret) |
| 631 | return ret; | 635 | goto out; |
| 632 | 636 | ||
| 633 | if (!ibs_eilvt_valid()) | 637 | if (!ibs_eilvt_valid()) { |
| 634 | return -EFAULT; | 638 | ret = -EFAULT; |
| 639 | goto out; | ||
| 640 | } | ||
| 635 | 641 | ||
| 642 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
| 636 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | 643 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); |
| 637 | 644 | ||
| 638 | return 0; | 645 | return 0; |
| 646 | out: | ||
| 647 | preempt_disable(); | ||
| 648 | put_eilvt(offset); | ||
| 649 | preempt_enable(); | ||
| 650 | return ret; | ||
| 639 | } | 651 | } |
| 640 | 652 | ||
| 641 | /* | 653 | /* |
| 642 | * check and reserve APIC extended interrupt LVT offset for IBS if | 654 | * check and reserve APIC extended interrupt LVT offset for IBS if |
| 643 | * available | 655 | * available |
| 644 | * | ||
| 645 | * init_ibs() preforms implicitly cpu-local operations, so pin this | ||
| 646 | * thread to its current CPU | ||
| 647 | */ | 656 | */ |
| 648 | 657 | ||
| 649 | static void init_ibs(void) | 658 | static void init_ibs(void) |
| 650 | { | 659 | { |
| 651 | preempt_disable(); | ||
| 652 | |||
| 653 | ibs_caps = get_ibs_caps(); | 660 | ibs_caps = get_ibs_caps(); |
| 661 | |||
| 654 | if (!ibs_caps) | 662 | if (!ibs_caps) |
| 663 | return; | ||
| 664 | |||
| 665 | if (ibs_eilvt_valid()) | ||
| 655 | goto out; | 666 | goto out; |
| 656 | 667 | ||
| 657 | if (__init_ibs_nmi() < 0) | 668 | if (!force_ibs_eilvt_setup()) |
| 658 | ibs_caps = 0; | 669 | goto out; |
| 659 | else | 670 | |
| 660 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); | 671 | /* Failed to setup ibs */ |
| 672 | ibs_caps = 0; | ||
| 673 | return; | ||
| 661 | 674 | ||
| 662 | out: | 675 | out: |
| 663 | preempt_enable(); | 676 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); |
| 664 | } | 677 | } |
| 665 | 678 | ||
| 666 | static int (*create_arch_files)(struct super_block *sb, struct dentry *root); | 679 | static int (*create_arch_files)(struct super_block *sb, struct dentry *root); |
