diff options
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 272 |
1 files changed, 228 insertions, 44 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 3b65f19a6681..6ebe9abf6aef 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -28,6 +28,7 @@ static bool perf_counters_initialized __read_mostly; | |||
28 | static int nr_counters_generic __read_mostly; | 28 | static int nr_counters_generic __read_mostly; |
29 | static u64 perf_counter_mask __read_mostly; | 29 | static u64 perf_counter_mask __read_mostly; |
30 | static u64 counter_value_mask __read_mostly; | 30 | static u64 counter_value_mask __read_mostly; |
31 | static int counter_value_bits __read_mostly; | ||
31 | 32 | ||
32 | static int nr_counters_fixed __read_mostly; | 33 | static int nr_counters_fixed __read_mostly; |
33 | 34 | ||
@@ -35,7 +36,9 @@ struct cpu_hw_counters { | |||
35 | struct perf_counter *counters[X86_PMC_IDX_MAX]; | 36 | struct perf_counter *counters[X86_PMC_IDX_MAX]; |
36 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 37 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
37 | unsigned long interrupts; | 38 | unsigned long interrupts; |
38 | u64 global_enable; | 39 | u64 throttle_ctrl; |
40 | u64 active_mask; | ||
41 | int enabled; | ||
39 | }; | 42 | }; |
40 | 43 | ||
41 | /* | 44 | /* |
@@ -43,21 +46,28 @@ struct cpu_hw_counters { | |||
43 | */ | 46 | */ |
44 | struct pmc_x86_ops { | 47 | struct pmc_x86_ops { |
45 | u64 (*save_disable_all)(void); | 48 | u64 (*save_disable_all)(void); |
46 | void (*restore_all)(u64 ctrl); | 49 | void (*restore_all)(u64); |
50 | u64 (*get_status)(u64); | ||
51 | void (*ack_status)(u64); | ||
52 | void (*enable)(int, u64); | ||
53 | void (*disable)(int, u64); | ||
47 | unsigned eventsel; | 54 | unsigned eventsel; |
48 | unsigned perfctr; | 55 | unsigned perfctr; |
49 | int (*event_map)(int event); | 56 | u64 (*event_map)(int); |
57 | u64 (*raw_event)(u64); | ||
50 | int max_events; | 58 | int max_events; |
51 | }; | 59 | }; |
52 | 60 | ||
53 | static struct pmc_x86_ops *pmc_ops; | 61 | static struct pmc_x86_ops *pmc_ops; |
54 | 62 | ||
55 | static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters); | 63 | static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = { |
64 | .enabled = 1, | ||
65 | }; | ||
56 | 66 | ||
57 | /* | 67 | /* |
58 | * Intel PerfMon v3. Used on Core2 and later. | 68 | * Intel PerfMon v3. Used on Core2 and later. |
59 | */ | 69 | */ |
60 | static const int intel_perfmon_event_map[] = | 70 | static const u64 intel_perfmon_event_map[] = |
61 | { | 71 | { |
62 | [PERF_COUNT_CPU_CYCLES] = 0x003c, | 72 | [PERF_COUNT_CPU_CYCLES] = 0x003c, |
63 | [PERF_COUNT_INSTRUCTIONS] = 0x00c0, | 73 | [PERF_COUNT_INSTRUCTIONS] = 0x00c0, |
@@ -68,15 +78,29 @@ static const int intel_perfmon_event_map[] = | |||
68 | [PERF_COUNT_BUS_CYCLES] = 0x013c, | 78 | [PERF_COUNT_BUS_CYCLES] = 0x013c, |
69 | }; | 79 | }; |
70 | 80 | ||
71 | static int pmc_intel_event_map(int event) | 81 | static u64 pmc_intel_event_map(int event) |
72 | { | 82 | { |
73 | return intel_perfmon_event_map[event]; | 83 | return intel_perfmon_event_map[event]; |
74 | } | 84 | } |
75 | 85 | ||
86 | static u64 pmc_intel_raw_event(u64 event) | ||
87 | { | ||
88 | #define CORE_EVNTSEL_EVENT_MASK 0x000000FF | ||
89 | #define CORE_EVNTSEL_UNIT_MASK 0x0000FF00 | ||
90 | #define CORE_EVNTSEL_COUNTER_MASK 0xFF000000 | ||
91 | |||
92 | #define CORE_EVNTSEL_MASK \ | ||
93 | (CORE_EVNTSEL_EVENT_MASK | \ | ||
94 | CORE_EVNTSEL_UNIT_MASK | \ | ||
95 | CORE_EVNTSEL_COUNTER_MASK) | ||
96 | |||
97 | return event & CORE_EVNTSEL_MASK; | ||
98 | } | ||
99 | |||
76 | /* | 100 | /* |
77 | * AMD Performance Monitor K7 and later. | 101 | * AMD Performance Monitor K7 and later. |
78 | */ | 102 | */ |
79 | static const int amd_perfmon_event_map[] = | 103 | static const u64 amd_perfmon_event_map[] = |
80 | { | 104 | { |
81 | [PERF_COUNT_CPU_CYCLES] = 0x0076, | 105 | [PERF_COUNT_CPU_CYCLES] = 0x0076, |
82 | [PERF_COUNT_INSTRUCTIONS] = 0x00c0, | 106 | [PERF_COUNT_INSTRUCTIONS] = 0x00c0, |
@@ -86,11 +110,25 @@ static const int amd_perfmon_event_map[] = | |||
86 | [PERF_COUNT_BRANCH_MISSES] = 0x00c5, | 110 | [PERF_COUNT_BRANCH_MISSES] = 0x00c5, |
87 | }; | 111 | }; |
88 | 112 | ||
89 | static int pmc_amd_event_map(int event) | 113 | static u64 pmc_amd_event_map(int event) |
90 | { | 114 | { |
91 | return amd_perfmon_event_map[event]; | 115 | return amd_perfmon_event_map[event]; |
92 | } | 116 | } |
93 | 117 | ||
118 | static u64 pmc_amd_raw_event(u64 event) | ||
119 | { | ||
120 | #define K7_EVNTSEL_EVENT_MASK 0x7000000FF | ||
121 | #define K7_EVNTSEL_UNIT_MASK 0x00000FF00 | ||
122 | #define K7_EVNTSEL_COUNTER_MASK 0x0FF000000 | ||
123 | |||
124 | #define K7_EVNTSEL_MASK \ | ||
125 | (K7_EVNTSEL_EVENT_MASK | \ | ||
126 | K7_EVNTSEL_UNIT_MASK | \ | ||
127 | K7_EVNTSEL_COUNTER_MASK) | ||
128 | |||
129 | return event & K7_EVNTSEL_MASK; | ||
130 | } | ||
131 | |||
94 | /* | 132 | /* |
95 | * Propagate counter elapsed time into the generic counter. | 133 | * Propagate counter elapsed time into the generic counter. |
96 | * Can only be executed on the CPU where the counter is active. | 134 | * Can only be executed on the CPU where the counter is active. |
@@ -179,7 +217,7 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
179 | * Raw event type provide the config in the event structure | 217 | * Raw event type provide the config in the event structure |
180 | */ | 218 | */ |
181 | if (hw_event->raw) { | 219 | if (hw_event->raw) { |
182 | hwc->config |= hw_event->type; | 220 | hwc->config |= pmc_ops->raw_event(hw_event->type); |
183 | } else { | 221 | } else { |
184 | if (hw_event->type >= pmc_ops->max_events) | 222 | if (hw_event->type >= pmc_ops->max_events) |
185 | return -EINVAL; | 223 | return -EINVAL; |
@@ -205,18 +243,24 @@ static u64 pmc_intel_save_disable_all(void) | |||
205 | 243 | ||
206 | static u64 pmc_amd_save_disable_all(void) | 244 | static u64 pmc_amd_save_disable_all(void) |
207 | { | 245 | { |
208 | int idx; | 246 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); |
209 | u64 val, ctrl = 0; | 247 | int enabled, idx; |
248 | |||
249 | enabled = cpuc->enabled; | ||
250 | cpuc->enabled = 0; | ||
251 | barrier(); | ||
210 | 252 | ||
211 | for (idx = 0; idx < nr_counters_generic; idx++) { | 253 | for (idx = 0; idx < nr_counters_generic; idx++) { |
254 | u64 val; | ||
255 | |||
212 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); | 256 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); |
213 | if (val & ARCH_PERFMON_EVENTSEL0_ENABLE) | 257 | if (val & ARCH_PERFMON_EVENTSEL0_ENABLE) { |
214 | ctrl |= (1 << idx); | 258 | val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; |
215 | val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; | 259 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); |
216 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); | 260 | } |
217 | } | 261 | } |
218 | 262 | ||
219 | return ctrl; | 263 | return enabled; |
220 | } | 264 | } |
221 | 265 | ||
222 | u64 hw_perf_save_disable(void) | 266 | u64 hw_perf_save_disable(void) |
@@ -226,6 +270,9 @@ u64 hw_perf_save_disable(void) | |||
226 | 270 | ||
227 | return pmc_ops->save_disable_all(); | 271 | return pmc_ops->save_disable_all(); |
228 | } | 272 | } |
273 | /* | ||
274 | * Exported because of ACPI idle | ||
275 | */ | ||
229 | EXPORT_SYMBOL_GPL(hw_perf_save_disable); | 276 | EXPORT_SYMBOL_GPL(hw_perf_save_disable); |
230 | 277 | ||
231 | static void pmc_intel_restore_all(u64 ctrl) | 278 | static void pmc_intel_restore_all(u64 ctrl) |
@@ -235,11 +282,18 @@ static void pmc_intel_restore_all(u64 ctrl) | |||
235 | 282 | ||
236 | static void pmc_amd_restore_all(u64 ctrl) | 283 | static void pmc_amd_restore_all(u64 ctrl) |
237 | { | 284 | { |
238 | u64 val; | 285 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); |
239 | int idx; | 286 | int idx; |
240 | 287 | ||
288 | cpuc->enabled = ctrl; | ||
289 | barrier(); | ||
290 | if (!ctrl) | ||
291 | return; | ||
292 | |||
241 | for (idx = 0; idx < nr_counters_generic; idx++) { | 293 | for (idx = 0; idx < nr_counters_generic; idx++) { |
242 | if (ctrl & (1 << idx)) { | 294 | if (test_bit(idx, (unsigned long *)&cpuc->active_mask)) { |
295 | u64 val; | ||
296 | |||
243 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); | 297 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); |
244 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | 298 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; |
245 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); | 299 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); |
@@ -254,8 +308,112 @@ void hw_perf_restore(u64 ctrl) | |||
254 | 308 | ||
255 | pmc_ops->restore_all(ctrl); | 309 | pmc_ops->restore_all(ctrl); |
256 | } | 310 | } |
311 | /* | ||
312 | * Exported because of ACPI idle | ||
313 | */ | ||
257 | EXPORT_SYMBOL_GPL(hw_perf_restore); | 314 | EXPORT_SYMBOL_GPL(hw_perf_restore); |
258 | 315 | ||
316 | static u64 pmc_intel_get_status(u64 mask) | ||
317 | { | ||
318 | u64 status; | ||
319 | |||
320 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); | ||
321 | |||
322 | return status; | ||
323 | } | ||
324 | |||
325 | static u64 pmc_amd_get_status(u64 mask) | ||
326 | { | ||
327 | u64 status = 0; | ||
328 | int idx; | ||
329 | |||
330 | for (idx = 0; idx < nr_counters_generic; idx++) { | ||
331 | s64 val; | ||
332 | |||
333 | if (!(mask & (1 << idx))) | ||
334 | continue; | ||
335 | |||
336 | rdmsrl(MSR_K7_PERFCTR0 + idx, val); | ||
337 | val <<= (64 - counter_value_bits); | ||
338 | if (val >= 0) | ||
339 | status |= (1 << idx); | ||
340 | } | ||
341 | |||
342 | return status; | ||
343 | } | ||
344 | |||
345 | static u64 hw_perf_get_status(u64 mask) | ||
346 | { | ||
347 | if (unlikely(!perf_counters_initialized)) | ||
348 | return 0; | ||
349 | |||
350 | return pmc_ops->get_status(mask); | ||
351 | } | ||
352 | |||
353 | static void pmc_intel_ack_status(u64 ack) | ||
354 | { | ||
355 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); | ||
356 | } | ||
357 | |||
358 | static void pmc_amd_ack_status(u64 ack) | ||
359 | { | ||
360 | } | ||
361 | |||
362 | static void hw_perf_ack_status(u64 ack) | ||
363 | { | ||
364 | if (unlikely(!perf_counters_initialized)) | ||
365 | return; | ||
366 | |||
367 | pmc_ops->ack_status(ack); | ||
368 | } | ||
369 | |||
370 | static void pmc_intel_enable(int idx, u64 config) | ||
371 | { | ||
372 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + idx, | ||
373 | config | ARCH_PERFMON_EVENTSEL0_ENABLE); | ||
374 | } | ||
375 | |||
376 | static void pmc_amd_enable(int idx, u64 config) | ||
377 | { | ||
378 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
379 | |||
380 | set_bit(idx, (unsigned long *)&cpuc->active_mask); | ||
381 | if (cpuc->enabled) | ||
382 | config |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
383 | |||
384 | wrmsrl(MSR_K7_EVNTSEL0 + idx, config); | ||
385 | } | ||
386 | |||
387 | static void hw_perf_enable(int idx, u64 config) | ||
388 | { | ||
389 | if (unlikely(!perf_counters_initialized)) | ||
390 | return; | ||
391 | |||
392 | pmc_ops->enable(idx, config); | ||
393 | } | ||
394 | |||
395 | static void pmc_intel_disable(int idx, u64 config) | ||
396 | { | ||
397 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + idx, config); | ||
398 | } | ||
399 | |||
400 | static void pmc_amd_disable(int idx, u64 config) | ||
401 | { | ||
402 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
403 | |||
404 | clear_bit(idx, (unsigned long *)&cpuc->active_mask); | ||
405 | wrmsrl(MSR_K7_EVNTSEL0 + idx, config); | ||
406 | |||
407 | } | ||
408 | |||
409 | static void hw_perf_disable(int idx, u64 config) | ||
410 | { | ||
411 | if (unlikely(!perf_counters_initialized)) | ||
412 | return; | ||
413 | |||
414 | pmc_ops->disable(idx, config); | ||
415 | } | ||
416 | |||
259 | static inline void | 417 | static inline void |
260 | __pmc_fixed_disable(struct perf_counter *counter, | 418 | __pmc_fixed_disable(struct perf_counter *counter, |
261 | struct hw_perf_counter *hwc, unsigned int __idx) | 419 | struct hw_perf_counter *hwc, unsigned int __idx) |
@@ -278,7 +436,7 @@ __pmc_generic_disable(struct perf_counter *counter, | |||
278 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) | 436 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) |
279 | __pmc_fixed_disable(counter, hwc, idx); | 437 | __pmc_fixed_disable(counter, hwc, idx); |
280 | else | 438 | else |
281 | wrmsr_safe(hwc->config_base + idx, hwc->config, 0); | 439 | hw_perf_disable(idx, hwc->config); |
282 | } | 440 | } |
283 | 441 | ||
284 | static DEFINE_PER_CPU(u64, prev_left[X86_PMC_IDX_MAX]); | 442 | static DEFINE_PER_CPU(u64, prev_left[X86_PMC_IDX_MAX]); |
@@ -354,8 +512,7 @@ __pmc_generic_enable(struct perf_counter *counter, | |||
354 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) | 512 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) |
355 | __pmc_fixed_enable(counter, hwc, idx); | 513 | __pmc_fixed_enable(counter, hwc, idx); |
356 | else | 514 | else |
357 | wrmsr(hwc->config_base + idx, | 515 | hw_perf_enable(idx, hwc->config); |
358 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE, 0); | ||
359 | } | 516 | } |
360 | 517 | ||
361 | static int | 518 | static int |
@@ -567,22 +724,20 @@ perf_handle_group(struct perf_counter *sibling, u64 *status, u64 *overflown) | |||
567 | * This handler is triggered by the local APIC, so the APIC IRQ handling | 724 | * This handler is triggered by the local APIC, so the APIC IRQ handling |
568 | * rules apply: | 725 | * rules apply: |
569 | */ | 726 | */ |
570 | static void __smp_perf_counter_interrupt(struct pt_regs *regs, int nmi) | 727 | static int __smp_perf_counter_interrupt(struct pt_regs *regs, int nmi) |
571 | { | 728 | { |
572 | int bit, cpu = smp_processor_id(); | 729 | int bit, cpu = smp_processor_id(); |
573 | u64 ack, status; | 730 | u64 ack, status; |
574 | struct cpu_hw_counters *cpuc = &per_cpu(cpu_hw_counters, cpu); | 731 | struct cpu_hw_counters *cpuc = &per_cpu(cpu_hw_counters, cpu); |
732 | int ret = 0; | ||
575 | 733 | ||
576 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, cpuc->global_enable); | 734 | cpuc->throttle_ctrl = hw_perf_save_disable(); |
577 | |||
578 | /* Disable counters globally */ | ||
579 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); | ||
580 | ack_APIC_irq(); | ||
581 | 735 | ||
582 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); | 736 | status = hw_perf_get_status(cpuc->throttle_ctrl); |
583 | if (!status) | 737 | if (!status) |
584 | goto out; | 738 | goto out; |
585 | 739 | ||
740 | ret = 1; | ||
586 | again: | 741 | again: |
587 | inc_irq_stat(apic_perf_irqs); | 742 | inc_irq_stat(apic_perf_irqs); |
588 | ack = status; | 743 | ack = status; |
@@ -618,12 +773,12 @@ again: | |||
618 | } | 773 | } |
619 | } | 774 | } |
620 | 775 | ||
621 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); | 776 | hw_perf_ack_status(ack); |
622 | 777 | ||
623 | /* | 778 | /* |
624 | * Repeat if there is more work to be done: | 779 | * Repeat if there is more work to be done: |
625 | */ | 780 | */ |
626 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); | 781 | status = hw_perf_get_status(cpuc->throttle_ctrl); |
627 | if (status) | 782 | if (status) |
628 | goto again; | 783 | goto again; |
629 | out: | 784 | out: |
@@ -631,32 +786,27 @@ out: | |||
631 | * Restore - do not reenable when global enable is off or throttled: | 786 | * Restore - do not reenable when global enable is off or throttled: |
632 | */ | 787 | */ |
633 | if (++cpuc->interrupts < PERFMON_MAX_INTERRUPTS) | 788 | if (++cpuc->interrupts < PERFMON_MAX_INTERRUPTS) |
634 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, cpuc->global_enable); | 789 | hw_perf_restore(cpuc->throttle_ctrl); |
790 | |||
791 | return ret; | ||
635 | } | 792 | } |
636 | 793 | ||
637 | void perf_counter_unthrottle(void) | 794 | void perf_counter_unthrottle(void) |
638 | { | 795 | { |
639 | struct cpu_hw_counters *cpuc; | 796 | struct cpu_hw_counters *cpuc; |
640 | u64 global_enable; | ||
641 | 797 | ||
642 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | 798 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) |
643 | return; | 799 | return; |
644 | 800 | ||
645 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) | ||
646 | return; | ||
647 | |||
648 | if (unlikely(!perf_counters_initialized)) | 801 | if (unlikely(!perf_counters_initialized)) |
649 | return; | 802 | return; |
650 | 803 | ||
651 | cpuc = &per_cpu(cpu_hw_counters, smp_processor_id()); | 804 | cpuc = &__get_cpu_var(cpu_hw_counters); |
652 | if (cpuc->interrupts >= PERFMON_MAX_INTERRUPTS) { | 805 | if (cpuc->interrupts >= PERFMON_MAX_INTERRUPTS) { |
653 | if (printk_ratelimit()) | 806 | if (printk_ratelimit()) |
654 | printk(KERN_WARNING "PERFMON: max interrupts exceeded!\n"); | 807 | printk(KERN_WARNING "PERFMON: max interrupts exceeded!\n"); |
655 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, cpuc->global_enable); | 808 | hw_perf_restore(cpuc->throttle_ctrl); |
656 | } | 809 | } |
657 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, global_enable); | ||
658 | if (unlikely(cpuc->global_enable && !global_enable)) | ||
659 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, cpuc->global_enable); | ||
660 | cpuc->interrupts = 0; | 810 | cpuc->interrupts = 0; |
661 | } | 811 | } |
662 | 812 | ||
@@ -664,8 +814,8 @@ void smp_perf_counter_interrupt(struct pt_regs *regs) | |||
664 | { | 814 | { |
665 | irq_enter(); | 815 | irq_enter(); |
666 | apic_write(APIC_LVTPC, LOCAL_PERF_VECTOR); | 816 | apic_write(APIC_LVTPC, LOCAL_PERF_VECTOR); |
817 | ack_APIC_irq(); | ||
667 | __smp_perf_counter_interrupt(regs, 0); | 818 | __smp_perf_counter_interrupt(regs, 0); |
668 | |||
669 | irq_exit(); | 819 | irq_exit(); |
670 | } | 820 | } |
671 | 821 | ||
@@ -722,16 +872,23 @@ perf_counter_nmi_handler(struct notifier_block *self, | |||
722 | { | 872 | { |
723 | struct die_args *args = __args; | 873 | struct die_args *args = __args; |
724 | struct pt_regs *regs; | 874 | struct pt_regs *regs; |
875 | int ret; | ||
876 | |||
877 | switch (cmd) { | ||
878 | case DIE_NMI: | ||
879 | case DIE_NMI_IPI: | ||
880 | break; | ||
725 | 881 | ||
726 | if (likely(cmd != DIE_NMI_IPI)) | 882 | default: |
727 | return NOTIFY_DONE; | 883 | return NOTIFY_DONE; |
884 | } | ||
728 | 885 | ||
729 | regs = args->regs; | 886 | regs = args->regs; |
730 | 887 | ||
731 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 888 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
732 | __smp_perf_counter_interrupt(regs, 1); | 889 | ret = __smp_perf_counter_interrupt(regs, 1); |
733 | 890 | ||
734 | return NOTIFY_STOP; | 891 | return ret ? NOTIFY_STOP : NOTIFY_OK; |
735 | } | 892 | } |
736 | 893 | ||
737 | static __read_mostly struct notifier_block perf_counter_nmi_notifier = { | 894 | static __read_mostly struct notifier_block perf_counter_nmi_notifier = { |
@@ -743,18 +900,28 @@ static __read_mostly struct notifier_block perf_counter_nmi_notifier = { | |||
743 | static struct pmc_x86_ops pmc_intel_ops = { | 900 | static struct pmc_x86_ops pmc_intel_ops = { |
744 | .save_disable_all = pmc_intel_save_disable_all, | 901 | .save_disable_all = pmc_intel_save_disable_all, |
745 | .restore_all = pmc_intel_restore_all, | 902 | .restore_all = pmc_intel_restore_all, |
903 | .get_status = pmc_intel_get_status, | ||
904 | .ack_status = pmc_intel_ack_status, | ||
905 | .enable = pmc_intel_enable, | ||
906 | .disable = pmc_intel_disable, | ||
746 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, | 907 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, |
747 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, | 908 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, |
748 | .event_map = pmc_intel_event_map, | 909 | .event_map = pmc_intel_event_map, |
910 | .raw_event = pmc_intel_raw_event, | ||
749 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), | 911 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), |
750 | }; | 912 | }; |
751 | 913 | ||
752 | static struct pmc_x86_ops pmc_amd_ops = { | 914 | static struct pmc_x86_ops pmc_amd_ops = { |
753 | .save_disable_all = pmc_amd_save_disable_all, | 915 | .save_disable_all = pmc_amd_save_disable_all, |
754 | .restore_all = pmc_amd_restore_all, | 916 | .restore_all = pmc_amd_restore_all, |
917 | .get_status = pmc_amd_get_status, | ||
918 | .ack_status = pmc_amd_ack_status, | ||
919 | .enable = pmc_amd_enable, | ||
920 | .disable = pmc_amd_disable, | ||
755 | .eventsel = MSR_K7_EVNTSEL0, | 921 | .eventsel = MSR_K7_EVNTSEL0, |
756 | .perfctr = MSR_K7_PERFCTR0, | 922 | .perfctr = MSR_K7_PERFCTR0, |
757 | .event_map = pmc_amd_event_map, | 923 | .event_map = pmc_amd_event_map, |
924 | .raw_event = pmc_amd_raw_event, | ||
758 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), | 925 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), |
759 | }; | 926 | }; |
760 | 927 | ||
@@ -787,8 +954,25 @@ static struct pmc_x86_ops *pmc_intel_init(void) | |||
787 | 954 | ||
788 | static struct pmc_x86_ops *pmc_amd_init(void) | 955 | static struct pmc_x86_ops *pmc_amd_init(void) |
789 | { | 956 | { |
957 | u64 old; | ||
958 | int bits; | ||
959 | |||
790 | nr_counters_generic = 4; | 960 | nr_counters_generic = 4; |
791 | nr_counters_fixed = 0; | 961 | nr_counters_fixed = 0; |
962 | counter_value_mask = ~0ULL; | ||
963 | |||
964 | rdmsrl(MSR_K7_PERFCTR0, old); | ||
965 | wrmsrl(MSR_K7_PERFCTR0, counter_value_mask); | ||
966 | /* | ||
967 | * read the truncated mask | ||
968 | */ | ||
969 | rdmsrl(MSR_K7_PERFCTR0, counter_value_mask); | ||
970 | wrmsrl(MSR_K7_PERFCTR0, old); | ||
971 | |||
972 | bits = 32 + fls(counter_value_mask >> 32); | ||
973 | if (bits == 32) | ||
974 | bits = fls((u32)counter_value_mask); | ||
975 | counter_value_bits = bits; | ||
792 | 976 | ||
793 | pr_info("AMD Performance Monitoring support detected.\n"); | 977 | pr_info("AMD Performance Monitoring support detected.\n"); |
794 | 978 | ||