Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Robert Richter and committed by
Ingo Molnar
b7169166 ee5789db

+297 -201
+2
arch/x86/include/asm/perf_event.h
··· 159 159 #define IBS_OP_MAX_CNT 0x0000FFFFULL 160 160 #define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */ 161 161 162 + extern u32 get_ibs_caps(void); 163 + 162 164 #ifdef CONFIG_PERF_EVENTS 163 165 extern void perf_events_lapic_init(void); 164 166
+1 -1
arch/x86/kernel/cpu/Makefile
··· 36 36 obj-$(CONFIG_X86_MCE) += mcheck/ 37 37 obj-$(CONFIG_MTRR) += mtrr/ 38 38 39 - obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o 39 + obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o perf_event_amd_ibs.o 40 40 41 41 quiet_cmd_mkcapflags = MKCAP $@ 42 42 cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@
+294
arch/x86/kernel/cpu/perf_event_amd_ibs.c
··· 1 + /* 2 + * Performance events - AMD IBS 3 + * 4 + * Copyright (C) 2011 Advanced Micro Devices, Inc., Robert Richter 5 + * 6 + * For licencing details see kernel-base/COPYING 7 + */ 8 + 9 + #include <linux/perf_event.h> 10 + #include <linux/module.h> 11 + #include <linux/pci.h> 12 + 13 + #include <asm/apic.h> 14 + 15 + static u32 ibs_caps; 16 + 17 + #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) 18 + 19 + static struct pmu perf_ibs; 20 + 21 + static int perf_ibs_init(struct perf_event *event) 22 + { 23 + if (perf_ibs.type != event->attr.type) 24 + return -ENOENT; 25 + return 0; 26 + } 27 + 28 + static int perf_ibs_add(struct perf_event *event, int flags) 29 + { 30 + return 0; 31 + } 32 + 33 + static void perf_ibs_del(struct perf_event *event, int flags) 34 + { 35 + } 36 + 37 + static struct pmu perf_ibs = { 38 + .event_init= perf_ibs_init, 39 + .add= perf_ibs_add, 40 + .del= perf_ibs_del, 41 + }; 42 + 43 + static __init int perf_event_ibs_init(void) 44 + { 45 + if (!ibs_caps) 46 + return -ENODEV; /* ibs not supported by the cpu */ 47 + 48 + perf_pmu_register(&perf_ibs, "ibs", -1); 49 + printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); 50 + 51 + return 0; 52 + } 53 + 54 + #else /* defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) */ 55 + 56 + static __init int perf_event_ibs_init(void) { return 0; } 57 + 58 + #endif 59 + 60 + /* IBS - apic initialization, for perf and oprofile */ 61 + 62 + static __init u32 __get_ibs_caps(void) 63 + { 64 + u32 caps; 65 + unsigned int max_level; 66 + 67 + if (!boot_cpu_has(X86_FEATURE_IBS)) 68 + return 0; 69 + 70 + /* check IBS cpuid feature flags */ 71 + max_level = cpuid_eax(0x80000000); 72 + if (max_level < IBS_CPUID_FEATURES) 73 + return IBS_CAPS_DEFAULT; 74 + 75 + caps = cpuid_eax(IBS_CPUID_FEATURES); 76 + if (!(caps & IBS_CAPS_AVAIL)) 77 + /* cpuid flags not valid */ 78 + return IBS_CAPS_DEFAULT; 79 + 80 + return caps; 81 + } 82 + 83 + u32 get_ibs_caps(void) 84 + { 85 + return ibs_caps; 86 + } 87 + 88 + EXPORT_SYMBOL(get_ibs_caps); 89 + 90 + static inline int get_eilvt(int offset) 91 + { 92 + return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); 93 + } 94 + 95 + static inline int put_eilvt(int offset) 96 + { 97 + return !setup_APIC_eilvt(offset, 0, 0, 1); 98 + } 99 + 100 + /* 101 + * Check and reserve APIC extended interrupt LVT offset for IBS if available. 102 + */ 103 + static inline int ibs_eilvt_valid(void) 104 + { 105 + int offset; 106 + u64 val; 107 + int valid = 0; 108 + 109 + preempt_disable(); 110 + 111 + rdmsrl(MSR_AMD64_IBSCTL, val); 112 + offset = val & IBSCTL_LVT_OFFSET_MASK; 113 + 114 + if (!(val & IBSCTL_LVT_OFFSET_VALID)) { 115 + pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", 116 + smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); 117 + goto out; 118 + } 119 + 120 + if (!get_eilvt(offset)) { 121 + pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", 122 + smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); 123 + goto out; 124 + } 125 + 126 + valid = 1; 127 + out: 128 + preempt_enable(); 129 + 130 + return valid; 131 + } 132 + 133 + static int setup_ibs_ctl(int ibs_eilvt_off) 134 + { 135 + struct pci_dev *cpu_cfg; 136 + int nodes; 137 + u32 value = 0; 138 + 139 + nodes = 0; 140 + cpu_cfg = NULL; 141 + do { 142 + cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, 143 + PCI_DEVICE_ID_AMD_10H_NB_MISC, 144 + cpu_cfg); 145 + if (!cpu_cfg) 146 + break; 147 + ++nodes; 148 + pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off 149 + | IBSCTL_LVT_OFFSET_VALID); 150 + pci_read_config_dword(cpu_cfg, IBSCTL, &value); 151 + if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { 152 + pci_dev_put(cpu_cfg); 153 + printk(KERN_DEBUG "Failed to setup IBS LVT offset, " 154 + "IBSCTL = 0x%08x\n", value); 155 + return -EINVAL; 156 + } 157 + } while (1); 158 + 159 + if (!nodes) { 160 + printk(KERN_DEBUG "No CPU node configured for IBS\n"); 161 + return -ENODEV; 162 + } 163 + 164 + return 0; 165 + } 166 + 167 + /* 168 + * This runs only on the current cpu. We try to find an LVT offset and 169 + * setup the local APIC. For this we must disable preemption. On 170 + * success we initialize all nodes with this offset. This updates then 171 + * the offset in the IBS_CTL per-node msr. The per-core APIC setup of 172 + * the IBS interrupt vector is handled by perf_ibs_cpu_notifier that 173 + * is using the new offset. 174 + */ 175 + static int force_ibs_eilvt_setup(void) 176 + { 177 + int offset; 178 + int ret; 179 + 180 + preempt_disable(); 181 + /* find the next free available EILVT entry, skip offset 0 */ 182 + for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { 183 + if (get_eilvt(offset)) 184 + break; 185 + } 186 + preempt_enable(); 187 + 188 + if (offset == APIC_EILVT_NR_MAX) { 189 + printk(KERN_DEBUG "No EILVT entry available\n"); 190 + return -EBUSY; 191 + } 192 + 193 + ret = setup_ibs_ctl(offset); 194 + if (ret) 195 + goto out; 196 + 197 + if (!ibs_eilvt_valid()) { 198 + ret = -EFAULT; 199 + goto out; 200 + } 201 + 202 + pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); 203 + pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); 204 + 205 + return 0; 206 + out: 207 + preempt_disable(); 208 + put_eilvt(offset); 209 + preempt_enable(); 210 + return ret; 211 + } 212 + 213 + static inline int get_ibs_lvt_offset(void) 214 + { 215 + u64 val; 216 + 217 + rdmsrl(MSR_AMD64_IBSCTL, val); 218 + if (!(val & IBSCTL_LVT_OFFSET_VALID)) 219 + return -EINVAL; 220 + 221 + return val & IBSCTL_LVT_OFFSET_MASK; 222 + } 223 + 224 + static void setup_APIC_ibs(void *dummy) 225 + { 226 + int offset; 227 + 228 + offset = get_ibs_lvt_offset(); 229 + if (offset < 0) 230 + goto failed; 231 + 232 + if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) 233 + return; 234 + failed: 235 + pr_warn("perf: IBS APIC setup failed on cpu #%d\n", 236 + smp_processor_id()); 237 + } 238 + 239 + static void clear_APIC_ibs(void *dummy) 240 + { 241 + int offset; 242 + 243 + offset = get_ibs_lvt_offset(); 244 + if (offset >= 0) 245 + setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); 246 + } 247 + 248 + static int __cpuinit 249 + perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) 250 + { 251 + switch (action & ~CPU_TASKS_FROZEN) { 252 + case CPU_STARTING: 253 + setup_APIC_ibs(NULL); 254 + break; 255 + case CPU_DYING: 256 + clear_APIC_ibs(NULL); 257 + break; 258 + default: 259 + break; 260 + } 261 + 262 + return NOTIFY_OK; 263 + } 264 + 265 + static __init int amd_ibs_init(void) 266 + { 267 + u32 caps; 268 + int ret; 269 + 270 + caps = __get_ibs_caps(); 271 + if (!caps) 272 + return -ENODEV; /* ibs not supported by the cpu */ 273 + 274 + if (!ibs_eilvt_valid()) { 275 + ret = force_ibs_eilvt_setup(); 276 + if (ret) { 277 + pr_err("Failed to setup IBS, %d\n", ret); 278 + return ret; 279 + } 280 + } 281 + 282 + get_online_cpus(); 283 + ibs_caps = caps; 284 + /* make ibs_caps visible to other cpus: */ 285 + smp_mb(); 286 + perf_cpu_notifier(perf_ibs_cpu_notifier); 287 + smp_call_function(setup_APIC_ibs, NULL, 1); 288 + put_online_cpus(); 289 + 290 + return perf_event_ibs_init(); 291 + } 292 + 293 + /* Since we need the pci subsystem to init ibs we can't do this earlier: */ 294 + device_initcall(amd_ibs_init);
-2
arch/x86/oprofile/nmi_int.c
··· 385 385 apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); 386 386 apic_write(APIC_LVTERR, v); 387 387 nmi_cpu_restore_registers(msrs); 388 - if (model->cpu_down) 389 - model->cpu_down(); 390 388 } 391 389 392 390 static void nmi_cpu_up(void *dummy)
-197
arch/x86/oprofile/op_model_amd.c
··· 74 74 #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) 75 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 78 * 16-bit Linear Feedback Shift Register (LFSR) 100 79 * ··· 264 285 wrmsrl(MSR_AMD64_IBSOPCTL, 0); 265 286 } 266 287 267 - static inline int get_eilvt(int offset) 268 - { 269 - return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); 270 - } 271 - 272 - static inline int put_eilvt(int offset) 273 - { 274 - return !setup_APIC_eilvt(offset, 0, 0, 1); 275 - } 276 - 277 - static inline int ibs_eilvt_valid(void) 278 - { 279 - int offset; 280 - u64 val; 281 - int valid = 0; 282 - 283 - preempt_disable(); 284 - 285 - rdmsrl(MSR_AMD64_IBSCTL, val); 286 - offset = val & IBSCTL_LVT_OFFSET_MASK; 287 - 288 - if (!(val & IBSCTL_LVT_OFFSET_VALID)) { 289 - pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", 290 - smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); 291 - goto out; 292 - } 293 - 294 - if (!get_eilvt(offset)) { 295 - pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", 296 - smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); 297 - goto out; 298 - } 299 - 300 - valid = 1; 301 - out: 302 - preempt_enable(); 303 - 304 - return valid; 305 - } 306 - 307 - static inline int get_ibs_offset(void) 308 - { 309 - u64 val; 310 - 311 - rdmsrl(MSR_AMD64_IBSCTL, val); 312 - if (!(val & IBSCTL_LVT_OFFSET_VALID)) 313 - return -EINVAL; 314 - 315 - return val & IBSCTL_LVT_OFFSET_MASK; 316 - } 317 - 318 - static void setup_APIC_ibs(void) 319 - { 320 - int offset; 321 - 322 - offset = get_ibs_offset(); 323 - if (offset < 0) 324 - goto failed; 325 - 326 - if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) 327 - return; 328 - failed: 329 - pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", 330 - smp_processor_id()); 331 - } 332 - 333 - static void clear_APIC_ibs(void) 334 - { 335 - int offset; 336 - 337 - offset = get_ibs_offset(); 338 - if (offset >= 0) 339 - setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); 340 - } 341 - 342 288 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 343 289 344 290 static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, ··· 377 473 val |= op_x86_get_ctrl(model, &counter_config[virt]); 378 474 wrmsrl(msrs->controls[i].addr, val); 379 475 } 380 - 381 - if (ibs_caps) 382 - setup_APIC_ibs(); 383 - } 384 - 385 - static void op_amd_cpu_shutdown(void) 386 - { 387 - if (ibs_caps) 388 - clear_APIC_ibs(); 389 476 } 390 477 391 478 static int op_amd_check_ctrs(struct pt_regs * const regs, ··· 439 544 op_amd_stop_ibs(); 440 545 } 441 546 442 - static int setup_ibs_ctl(int ibs_eilvt_off) 443 - { 444 - struct pci_dev *cpu_cfg; 445 - int nodes; 446 - u32 value = 0; 447 - 448 - nodes = 0; 449 - cpu_cfg = NULL; 450 - do { 451 - cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, 452 - PCI_DEVICE_ID_AMD_10H_NB_MISC, 453 - cpu_cfg); 454 - if (!cpu_cfg) 455 - break; 456 - ++nodes; 457 - pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off 458 - | IBSCTL_LVT_OFFSET_VALID); 459 - pci_read_config_dword(cpu_cfg, IBSCTL, &value); 460 - if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { 461 - pci_dev_put(cpu_cfg); 462 - printk(KERN_DEBUG "Failed to setup IBS LVT offset, " 463 - "IBSCTL = 0x%08x\n", value); 464 - return -EINVAL; 465 - } 466 - } while (1); 467 - 468 - if (!nodes) { 469 - printk(KERN_DEBUG "No CPU node configured for IBS\n"); 470 - return -ENODEV; 471 - } 472 - 473 - return 0; 474 - } 475 - 476 - /* 477 - * This runs only on the current cpu. We try to find an LVT offset and 478 - * setup the local APIC. For this we must disable preemption. On 479 - * success we initialize all nodes with this offset. This updates then 480 - * the offset in the IBS_CTL per-node msr. The per-core APIC setup of 481 - * the IBS interrupt vector is called from op_amd_setup_ctrs()/op_- 482 - * amd_cpu_shutdown() using the new offset. 483 - */ 484 - static int force_ibs_eilvt_setup(void) 485 - { 486 - int offset; 487 - int ret; 488 - 489 - preempt_disable(); 490 - /* find the next free available EILVT entry, skip offset 0 */ 491 - for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { 492 - if (get_eilvt(offset)) 493 - break; 494 - } 495 - preempt_enable(); 496 - 497 - if (offset == APIC_EILVT_NR_MAX) { 498 - printk(KERN_DEBUG "No EILVT entry available\n"); 499 - return -EBUSY; 500 - } 501 - 502 - ret = setup_ibs_ctl(offset); 503 - if (ret) 504 - goto out; 505 - 506 - if (!ibs_eilvt_valid()) { 507 - ret = -EFAULT; 508 - goto out; 509 - } 510 - 511 - pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); 512 - pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); 513 - 514 - return 0; 515 - out: 516 - preempt_disable(); 517 - put_eilvt(offset); 518 - preempt_enable(); 519 - return ret; 520 - } 521 - 522 547 /* 523 548 * check and reserve APIC extended interrupt LVT offset for IBS if 524 549 * available ··· 451 636 if (!ibs_caps) 452 637 return; 453 638 454 - if (ibs_eilvt_valid()) 455 - goto out; 456 - 457 - if (!force_ibs_eilvt_setup()) 458 - goto out; 459 - 460 - /* Failed to setup ibs */ 461 - ibs_caps = 0; 462 - return; 463 - 464 - out: 465 639 printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); 466 640 } 467 641 ··· 533 729 .init = op_amd_init, 534 730 .fill_in_addresses = &op_amd_fill_in_addresses, 535 731 .setup_ctrs = &op_amd_setup_ctrs, 536 - .cpu_down = &op_amd_cpu_shutdown, 537 732 .check_ctrs = &op_amd_check_ctrs, 538 733 .start = &op_amd_start, 539 734 .stop = &op_amd_stop,
-1
arch/x86/oprofile/op_x86_model.h
··· 43 43 int (*fill_in_addresses)(struct op_msrs * const msrs); 44 44 void (*setup_ctrs)(struct op_x86_model_spec const *model, 45 45 struct op_msrs const * const msrs); 46 - void (*cpu_down)(void); 47 46 int (*check_ctrs)(struct pt_regs * const regs, 48 47 struct op_msrs const * const msrs); 49 48 void (*start)(struct op_msrs const * const msrs);