diff options
-rw-r--r-- | arch/x86/kernel/cpu/amd.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 83 |
2 files changed, 85 insertions, 2 deletions
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 25423a5b80ed..edcde52bd170 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
@@ -368,6 +368,10 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) | |||
368 | if (c->x86 >= 6) | 368 | if (c->x86 >= 6) |
369 | set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK); | 369 | set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK); |
370 | 370 | ||
371 | /* Enable Performance counter for K7 and later */ | ||
372 | if (c->x86 > 6 && c->x86 <= 0x11) | ||
373 | set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON); | ||
374 | |||
371 | if (!c->x86_model_id[0]) { | 375 | if (!c->x86_model_id[0]) { |
372 | switch (c->x86) { | 376 | switch (c->x86) { |
373 | case 0xf: | 377 | case 0xf: |
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index a3c88529bb72..266618aa1a03 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -74,6 +74,24 @@ static int pmc_intel_event_map(int event) | |||
74 | } | 74 | } |
75 | 75 | ||
76 | /* | 76 | /* |
77 | * AMD Performance Monitor K7 and later. | ||
78 | */ | ||
79 | static const int amd_perfmon_event_map[] = | ||
80 | { | ||
81 | [PERF_COUNT_CPU_CYCLES] = 0x0076, | ||
82 | [PERF_COUNT_INSTRUCTIONS] = 0x00c0, | ||
83 | [PERF_COUNT_CACHE_REFERENCES] = 0x0080, | ||
84 | [PERF_COUNT_CACHE_MISSES] = 0x0081, | ||
85 | [PERF_COUNT_BRANCH_INSTRUCTIONS] = 0x00c4, | ||
86 | [PERF_COUNT_BRANCH_MISSES] = 0x00c5, | ||
87 | }; | ||
88 | |||
89 | static int pmc_amd_event_map(int event) | ||
90 | { | ||
91 | return amd_perfmon_event_map[event]; | ||
92 | } | ||
93 | |||
94 | /* | ||
77 | * Propagate counter elapsed time into the generic counter. | 95 | * Propagate counter elapsed time into the generic counter. |
78 | * Can only be executed on the CPU where the counter is active. | 96 | * Can only be executed on the CPU where the counter is active. |
79 | * Returns the delta events processed. | 97 | * Returns the delta events processed. |
@@ -151,8 +169,9 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
151 | * so we install an artificial 1<<31 period regardless of | 169 | * so we install an artificial 1<<31 period regardless of |
152 | * the generic counter period: | 170 | * the generic counter period: |
153 | */ | 171 | */ |
154 | if ((s64)hwc->irq_period <= 0 || hwc->irq_period > 0x7FFFFFFF) | 172 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) |
155 | hwc->irq_period = 0x7FFFFFFF; | 173 | if ((s64)hwc->irq_period <= 0 || hwc->irq_period > 0x7FFFFFFF) |
174 | hwc->irq_period = 0x7FFFFFFF; | ||
156 | 175 | ||
157 | atomic64_set(&hwc->period_left, hwc->irq_period); | 176 | atomic64_set(&hwc->period_left, hwc->irq_period); |
158 | 177 | ||
@@ -184,6 +203,22 @@ static u64 pmc_intel_save_disable_all(void) | |||
184 | return ctrl; | 203 | return ctrl; |
185 | } | 204 | } |
186 | 205 | ||
206 | static u64 pmc_amd_save_disable_all(void) | ||
207 | { | ||
208 | int idx; | ||
209 | u64 val, ctrl = 0; | ||
210 | |||
211 | for (idx = 0; idx < nr_counters_generic; idx++) { | ||
212 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); | ||
213 | if (val & ARCH_PERFMON_EVENTSEL0_ENABLE) | ||
214 | ctrl |= (1 << idx); | ||
215 | val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
216 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); | ||
217 | } | ||
218 | |||
219 | return ctrl; | ||
220 | } | ||
221 | |||
187 | u64 hw_perf_save_disable(void) | 222 | u64 hw_perf_save_disable(void) |
188 | { | 223 | { |
189 | if (unlikely(!perf_counters_initialized)) | 224 | if (unlikely(!perf_counters_initialized)) |
@@ -198,6 +233,20 @@ static void pmc_intel_restore_all(u64 ctrl) | |||
198 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | 233 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); |
199 | } | 234 | } |
200 | 235 | ||
236 | static void pmc_amd_restore_all(u64 ctrl) | ||
237 | { | ||
238 | u64 val; | ||
239 | int idx; | ||
240 | |||
241 | for (idx = 0; idx < nr_counters_generic; idx++) { | ||
242 | if (ctrl & (1 << idx)) { | ||
243 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); | ||
244 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
245 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
201 | void hw_perf_restore(u64 ctrl) | 250 | void hw_perf_restore(u64 ctrl) |
202 | { | 251 | { |
203 | if (unlikely(!perf_counters_initialized)) | 252 | if (unlikely(!perf_counters_initialized)) |
@@ -314,6 +363,9 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc) | |||
314 | { | 363 | { |
315 | unsigned int event; | 364 | unsigned int event; |
316 | 365 | ||
366 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) | ||
367 | return -1; | ||
368 | |||
317 | if (unlikely(hwc->nmi)) | 369 | if (unlikely(hwc->nmi)) |
318 | return -1; | 370 | return -1; |
319 | 371 | ||
@@ -401,6 +453,7 @@ void perf_counter_print_debug(void) | |||
401 | cpu = smp_processor_id(); | 453 | cpu = smp_processor_id(); |
402 | cpuc = &per_cpu(cpu_hw_counters, cpu); | 454 | cpuc = &per_cpu(cpu_hw_counters, cpu); |
403 | 455 | ||
456 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) { | ||
404 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | 457 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); |
405 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); | 458 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); |
406 | rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); | 459 | rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); |
@@ -411,6 +464,7 @@ void perf_counter_print_debug(void) | |||
411 | printk(KERN_INFO "CPU#%d: status: %016llx\n", cpu, status); | 464 | printk(KERN_INFO "CPU#%d: status: %016llx\n", cpu, status); |
412 | printk(KERN_INFO "CPU#%d: overflow: %016llx\n", cpu, overflow); | 465 | printk(KERN_INFO "CPU#%d: overflow: %016llx\n", cpu, overflow); |
413 | printk(KERN_INFO "CPU#%d: fixed: %016llx\n", cpu, fixed); | 466 | printk(KERN_INFO "CPU#%d: fixed: %016llx\n", cpu, fixed); |
467 | } | ||
414 | printk(KERN_INFO "CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used); | 468 | printk(KERN_INFO "CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used); |
415 | 469 | ||
416 | for (idx = 0; idx < nr_counters_generic; idx++) { | 470 | for (idx = 0; idx < nr_counters_generic; idx++) { |
@@ -588,6 +642,9 @@ void perf_counter_unthrottle(void) | |||
588 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | 642 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) |
589 | return; | 643 | return; |
590 | 644 | ||
645 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) | ||
646 | return; | ||
647 | |||
591 | if (unlikely(!perf_counters_initialized)) | 648 | if (unlikely(!perf_counters_initialized)) |
592 | return; | 649 | return; |
593 | 650 | ||
@@ -692,6 +749,15 @@ static struct pmc_x86_ops pmc_intel_ops = { | |||
692 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), | 749 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), |
693 | }; | 750 | }; |
694 | 751 | ||
752 | static struct pmc_x86_ops pmc_amd_ops = { | ||
753 | .save_disable_all = pmc_amd_save_disable_all, | ||
754 | .restore_all = pmc_amd_restore_all, | ||
755 | .eventsel = MSR_K7_EVNTSEL0, | ||
756 | .perfctr = MSR_K7_PERFCTR0, | ||
757 | .event_map = pmc_amd_event_map, | ||
758 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), | ||
759 | }; | ||
760 | |||
695 | static struct pmc_x86_ops *pmc_intel_init(void) | 761 | static struct pmc_x86_ops *pmc_intel_init(void) |
696 | { | 762 | { |
697 | union cpuid10_eax eax; | 763 | union cpuid10_eax eax; |
@@ -719,6 +785,16 @@ static struct pmc_x86_ops *pmc_intel_init(void) | |||
719 | return &pmc_intel_ops; | 785 | return &pmc_intel_ops; |
720 | } | 786 | } |
721 | 787 | ||
788 | static struct pmc_x86_ops *pmc_amd_init(void) | ||
789 | { | ||
790 | nr_counters_generic = 4; | ||
791 | nr_counters_fixed = 0; | ||
792 | |||
793 | printk(KERN_INFO "AMD Performance Monitoring support detected.\n"); | ||
794 | |||
795 | return &pmc_amd_ops; | ||
796 | } | ||
797 | |||
722 | void __init init_hw_perf_counters(void) | 798 | void __init init_hw_perf_counters(void) |
723 | { | 799 | { |
724 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | 800 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) |
@@ -728,6 +804,9 @@ void __init init_hw_perf_counters(void) | |||
728 | case X86_VENDOR_INTEL: | 804 | case X86_VENDOR_INTEL: |
729 | pmc_ops = pmc_intel_init(); | 805 | pmc_ops = pmc_intel_init(); |
730 | break; | 806 | break; |
807 | case X86_VENDOR_AMD: | ||
808 | pmc_ops = pmc_amd_init(); | ||
809 | break; | ||
731 | } | 810 | } |
732 | if (!pmc_ops) | 811 | if (!pmc_ops) |
733 | return; | 812 | return; |