diff options
| -rw-r--r-- | arch/x86/include/asm/apic.h | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/apic/apic.c | 19 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce_amd.c | 27 | ||||
| -rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 145 |
4 files changed, 154 insertions, 41 deletions
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index 1fa03e04ae44..286de34b0ed6 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h | |||
| @@ -252,9 +252,7 @@ static inline int apic_is_clustered_box(void) | |||
| 252 | } | 252 | } |
| 253 | #endif | 253 | #endif |
| 254 | 254 | ||
| 255 | extern u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask); | 255 | extern int setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask); |
| 256 | extern u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask); | ||
| 257 | |||
| 258 | 256 | ||
| 259 | #else /* !CONFIG_X86_LOCAL_APIC */ | 257 | #else /* !CONFIG_X86_LOCAL_APIC */ |
| 260 | static inline void lapic_shutdown(void) { } | 258 | static inline void lapic_shutdown(void) { } |
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 2bfeafd24f5c..850657d1b0ed 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c | |||
| @@ -390,9 +390,6 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) | |||
| 390 | * necessarily a BIOS bug. | 390 | * necessarily a BIOS bug. |
| 391 | */ | 391 | */ |
| 392 | 392 | ||
| 393 | #define APIC_EILVT_LVTOFF_MCE 0 | ||
| 394 | #define APIC_EILVT_LVTOFF_IBS 1 | ||
| 395 | |||
| 396 | static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX]; | 393 | static atomic_t eilvt_offsets[APIC_EILVT_NR_MAX]; |
| 397 | 394 | ||
| 398 | static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new) | 395 | static inline int eilvt_entry_is_changeable(unsigned int old, unsigned int new) |
| @@ -426,7 +423,7 @@ static unsigned int reserve_eilvt_offset(int offset, unsigned int new) | |||
| 426 | * enables the vector. See also the BKDGs. | 423 | * enables the vector. See also the BKDGs. |
| 427 | */ | 424 | */ |
| 428 | 425 | ||
| 429 | static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask) | 426 | int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask) |
| 430 | { | 427 | { |
| 431 | unsigned long reg = APIC_EILVTn(offset); | 428 | unsigned long reg = APIC_EILVTn(offset); |
| 432 | unsigned int new, old, reserved; | 429 | unsigned int new, old, reserved; |
| @@ -454,19 +451,7 @@ static int setup_APIC_eilvt(u8 offset, u8 vector, u8 msg_type, u8 mask) | |||
| 454 | 451 | ||
| 455 | return 0; | 452 | return 0; |
| 456 | } | 453 | } |
| 457 | 454 | EXPORT_SYMBOL_GPL(setup_APIC_eilvt); | |
| 458 | u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask) | ||
| 459 | { | ||
| 460 | setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask); | ||
| 461 | return APIC_EILVT_LVTOFF_MCE; | ||
| 462 | } | ||
| 463 | |||
| 464 | u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) | ||
| 465 | { | ||
| 466 | setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); | ||
| 467 | return APIC_EILVT_LVTOFF_IBS; | ||
| 468 | } | ||
| 469 | EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); | ||
| 470 | 455 | ||
| 471 | /* | 456 | /* |
| 472 | * Program the next event, relative to now | 457 | * Program the next event, relative to now |
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index 39aaee5c1ab2..80c482382d5c 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c | |||
| @@ -131,7 +131,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c) | |||
| 131 | u32 low = 0, high = 0, address = 0; | 131 | u32 low = 0, high = 0, address = 0; |
| 132 | unsigned int bank, block; | 132 | unsigned int bank, block; |
| 133 | struct thresh_restart tr; | 133 | struct thresh_restart tr; |
| 134 | u8 lvt_off; | 134 | int lvt_off = -1; |
| 135 | u8 offset; | ||
| 135 | 136 | ||
| 136 | for (bank = 0; bank < NR_BANKS; ++bank) { | 137 | for (bank = 0; bank < NR_BANKS; ++bank) { |
| 137 | for (block = 0; block < NR_BLOCKS; ++block) { | 138 | for (block = 0; block < NR_BLOCKS; ++block) { |
| @@ -162,8 +163,28 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c) | |||
| 162 | if (shared_bank[bank] && c->cpu_core_id) | 163 | if (shared_bank[bank] && c->cpu_core_id) |
| 163 | break; | 164 | break; |
| 164 | #endif | 165 | #endif |
| 165 | lvt_off = setup_APIC_eilvt_mce(THRESHOLD_APIC_VECTOR, | 166 | offset = (high & MASK_LVTOFF_HI) >> 20; |
| 166 | APIC_EILVT_MSG_FIX, 0); | 167 | if (lvt_off < 0) { |
| 168 | if (setup_APIC_eilvt(offset, | ||
| 169 | THRESHOLD_APIC_VECTOR, | ||
| 170 | APIC_EILVT_MSG_FIX, 0)) { | ||
| 171 | pr_err(FW_BUG "cpu %d, failed to " | ||
| 172 | "setup threshold interrupt " | ||
| 173 | "for bank %d, block %d " | ||
| 174 | "(MSR%08X=0x%x%08x)", | ||
| 175 | smp_processor_id(), bank, block, | ||
| 176 | address, high, low); | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | lvt_off = offset; | ||
| 180 | } else if (lvt_off != offset) { | ||
| 181 | pr_err(FW_BUG "cpu %d, invalid threshold " | ||
| 182 | "interrupt offset %d for bank %d," | ||
| 183 | "block %d (MSR%08X=0x%x%08x)", | ||
| 184 | smp_processor_id(), lvt_off, bank, | ||
| 185 | block, address, high, low); | ||
| 186 | continue; | ||
| 187 | } | ||
| 167 | 188 | ||
| 168 | high &= ~MASK_LVTOFF_HI; | 189 | high &= ~MASK_LVTOFF_HI; |
| 169 | high |= lvt_off << 20; | 190 | high |= lvt_off << 20; |
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index b67a6b5aa8d4..42fb46f83883 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
| @@ -64,15 +64,22 @@ static u64 ibs_op_ctl; | |||
| 64 | * IBS cpuid feature detection | 64 | * IBS cpuid feature detection |
| 65 | */ | 65 | */ |
| 66 | 66 | ||
| 67 | #define IBS_CPUID_FEATURES 0x8000001b | 67 | #define IBS_CPUID_FEATURES 0x8000001b |
| 68 | 68 | ||
| 69 | /* | 69 | /* |
| 70 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | 70 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but |
| 71 | * bit 0 is used to indicate the existence of IBS. | 71 | * bit 0 is used to indicate the existence of IBS. |
| 72 | */ | 72 | */ |
| 73 | #define IBS_CAPS_AVAIL (1LL<<0) | 73 | #define IBS_CAPS_AVAIL (1U<<0) |
| 74 | #define IBS_CAPS_RDWROPCNT (1LL<<3) | 74 | #define IBS_CAPS_RDWROPCNT (1U<<3) |
| 75 | #define IBS_CAPS_OPCNT (1LL<<4) | 75 | #define IBS_CAPS_OPCNT (1U<<4) |
| 76 | |||
| 77 | /* | ||
| 78 | * IBS APIC setup | ||
| 79 | */ | ||
| 80 | #define IBSCTL 0x1cc | ||
| 81 | #define IBSCTL_LVT_OFFSET_VALID (1ULL<<8) | ||
| 82 | #define IBSCTL_LVT_OFFSET_MASK 0x0F | ||
| 76 | 83 | ||
| 77 | /* | 84 | /* |
| 78 | * IBS randomization macros | 85 | * IBS randomization macros |
| @@ -266,6 +273,74 @@ static void op_amd_stop_ibs(void) | |||
| 266 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 273 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
| 267 | } | 274 | } |
| 268 | 275 | ||
| 276 | static inline int eilvt_is_available(int offset) | ||
| 277 | { | ||
| 278 | /* check if we may assign a vector */ | ||
| 279 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
| 280 | } | ||
| 281 | |||
| 282 | static inline int ibs_eilvt_valid(void) | ||
| 283 | { | ||
| 284 | u64 val; | ||
| 285 | int offset; | ||
| 286 | |||
| 287 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
| 288 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
| 289 | pr_err(FW_BUG "cpu %d, invalid IBS " | ||
| 290 | "interrupt offset %d (MSR%08X=0x%016llx)", | ||
| 291 | smp_processor_id(), offset, | ||
| 292 | MSR_AMD64_IBSCTL, val); | ||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
| 297 | |||
| 298 | if (eilvt_is_available(offset)) | ||
| 299 | return !0; | ||
| 300 | |||
| 301 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d " | ||
| 302 | "not available (MSR%08X=0x%016llx)", | ||
| 303 | smp_processor_id(), offset, | ||
| 304 | MSR_AMD64_IBSCTL, val); | ||
| 305 | |||
| 306 | return 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | static inline int get_ibs_offset(void) | ||
| 310 | { | ||
| 311 | u64 val; | ||
| 312 | |||
| 313 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
| 314 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
| 315 | return -EINVAL; | ||
| 316 | |||
| 317 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
| 318 | } | ||
| 319 | |||
| 320 | static void setup_APIC_ibs(void) | ||
| 321 | { | ||
| 322 | int offset; | ||
| 323 | |||
| 324 | offset = get_ibs_offset(); | ||
| 325 | if (offset < 0) | ||
| 326 | goto failed; | ||
| 327 | |||
| 328 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
| 329 | return; | ||
| 330 | failed: | ||
| 331 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
| 332 | smp_processor_id()); | ||
| 333 | } | ||
| 334 | |||
| 335 | static void clear_APIC_ibs(void) | ||
| 336 | { | ||
| 337 | int offset; | ||
| 338 | |||
| 339 | offset = get_ibs_offset(); | ||
| 340 | if (offset >= 0) | ||
| 341 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
| 342 | } | ||
| 343 | |||
| 269 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 344 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
| 270 | 345 | ||
| 271 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 346 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
| @@ -376,13 +451,13 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
| 376 | } | 451 | } |
| 377 | 452 | ||
| 378 | if (ibs_caps) | 453 | if (ibs_caps) |
| 379 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); | 454 | setup_APIC_ibs(); |
| 380 | } | 455 | } |
| 381 | 456 | ||
| 382 | static void op_amd_cpu_shutdown(void) | 457 | static void op_amd_cpu_shutdown(void) |
| 383 | { | 458 | { |
| 384 | if (ibs_caps) | 459 | if (ibs_caps) |
| 385 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | 460 | clear_APIC_ibs(); |
| 386 | } | 461 | } |
| 387 | 462 | ||
| 388 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 463 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
| @@ -445,16 +520,11 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
| 445 | op_amd_stop_ibs(); | 520 | op_amd_stop_ibs(); |
| 446 | } | 521 | } |
| 447 | 522 | ||
| 448 | static int __init_ibs_nmi(void) | 523 | static int setup_ibs_ctl(int ibs_eilvt_off) |
| 449 | { | 524 | { |
| 450 | #define IBSCTL_LVTOFFSETVAL (1 << 8) | ||
| 451 | #define IBSCTL 0x1cc | ||
| 452 | struct pci_dev *cpu_cfg; | 525 | struct pci_dev *cpu_cfg; |
| 453 | int nodes; | 526 | int nodes; |
| 454 | u32 value = 0; | 527 | u32 value = 0; |
| 455 | u8 ibs_eilvt_off; | ||
| 456 | |||
| 457 | ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | ||
| 458 | 528 | ||
| 459 | nodes = 0; | 529 | nodes = 0; |
| 460 | cpu_cfg = NULL; | 530 | cpu_cfg = NULL; |
| @@ -466,21 +536,60 @@ static int __init_ibs_nmi(void) | |||
| 466 | break; | 536 | break; |
| 467 | ++nodes; | 537 | ++nodes; |
| 468 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | 538 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off |
| 469 | | IBSCTL_LVTOFFSETVAL); | 539 | | IBSCTL_LVT_OFFSET_VALID); |
| 470 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | 540 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); |
| 471 | if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | 541 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { |
| 472 | pci_dev_put(cpu_cfg); | 542 | pci_dev_put(cpu_cfg); |
| 473 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | 543 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " |
| 474 | "IBSCTL = 0x%08x", value); | 544 | "IBSCTL = 0x%08x\n", value); |
| 475 | return 1; | 545 | return -EINVAL; |
| 476 | } | 546 | } |
| 477 | } while (1); | 547 | } while (1); |
| 478 | 548 | ||
| 479 | if (!nodes) { | 549 | if (!nodes) { |
| 480 | printk(KERN_DEBUG "No CPU node configured for IBS"); | 550 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); |
| 481 | return 1; | 551 | return -ENODEV; |
| 552 | } | ||
| 553 | |||
| 554 | return 0; | ||
| 555 | } | ||
| 556 | |||
| 557 | static int force_ibs_eilvt_setup(void) | ||
| 558 | { | ||
| 559 | int i; | ||
| 560 | int ret; | ||
| 561 | |||
| 562 | /* find the next free available EILVT entry */ | ||
| 563 | for (i = 1; i < 4; i++) { | ||
| 564 | if (!eilvt_is_available(i)) | ||
| 565 | continue; | ||
| 566 | ret = setup_ibs_ctl(i); | ||
| 567 | if (ret) | ||
| 568 | return ret; | ||
| 569 | return 0; | ||
| 482 | } | 570 | } |
| 483 | 571 | ||
| 572 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
| 573 | |||
| 574 | return -EBUSY; | ||
| 575 | } | ||
| 576 | |||
| 577 | static int __init_ibs_nmi(void) | ||
| 578 | { | ||
| 579 | int ret; | ||
| 580 | |||
| 581 | if (ibs_eilvt_valid()) | ||
| 582 | return 0; | ||
| 583 | |||
| 584 | ret = force_ibs_eilvt_setup(); | ||
| 585 | if (ret) | ||
| 586 | return ret; | ||
| 587 | |||
| 588 | if (!ibs_eilvt_valid()) | ||
| 589 | return -EFAULT; | ||
| 590 | |||
| 591 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
| 592 | |||
| 484 | return 0; | 593 | return 0; |
| 485 | } | 594 | } |
| 486 | 595 | ||
