diff options
author | Jaswinder Singh Rajput <jaswinderrajput@gmail.com> | 2009-02-27 07:39:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-28 04:38:27 -0500 |
commit | b56a3802dc6df29aa27d2c12edf420258091ad66 (patch) | |
tree | 0cc92cd879cb24e931565dbe05c0a22207ced750 /arch/x86/kernel/cpu/perf_counter.c | |
parent | 0c489c47d45ecac21059567c163f92138b2fbaa2 (diff) |
x86: prepare perf_counter to add more cpus
Introduced struct pmc_x86_ops to add more cpus.
Signed-off-by: Jaswinder Singh Rajput <jaswinderrajput@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/cpu/perf_counter.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 106 |
1 files changed, 78 insertions, 28 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 383d4c6423a1..a3c88529bb72 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright(C) 2008 Thomas Gleixner <tglx@linutronix.de> | 4 | * Copyright(C) 2008 Thomas Gleixner <tglx@linutronix.de> |
5 | * Copyright(C) 2008 Red Hat, Inc., Ingo Molnar | 5 | * Copyright(C) 2008 Red Hat, Inc., Ingo Molnar |
6 | * Copyright(C) 2009 Jaswinder Singh Rajput | ||
6 | * | 7 | * |
7 | * For licencing details see kernel-base/COPYING | 8 | * For licencing details see kernel-base/COPYING |
8 | */ | 9 | */ |
@@ -38,10 +39,24 @@ struct cpu_hw_counters { | |||
38 | }; | 39 | }; |
39 | 40 | ||
40 | /* | 41 | /* |
41 | * Intel PerfMon v3. Used on Core2 and later. | 42 | * struct pmc_x86_ops - performance counter x86 ops |
42 | */ | 43 | */ |
44 | struct pmc_x86_ops { | ||
45 | u64 (*save_disable_all) (void); | ||
46 | void (*restore_all) (u64 ctrl); | ||
47 | unsigned eventsel; | ||
48 | unsigned perfctr; | ||
49 | int (*event_map) (int event); | ||
50 | int max_events; | ||
51 | }; | ||
52 | |||
53 | static struct pmc_x86_ops *pmc_ops; | ||
54 | |||
43 | static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters); | 55 | static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters); |
44 | 56 | ||
57 | /* | ||
58 | * Intel PerfMon v3. Used on Core2 and later. | ||
59 | */ | ||
45 | static const int intel_perfmon_event_map[] = | 60 | static const int intel_perfmon_event_map[] = |
46 | { | 61 | { |
47 | [PERF_COUNT_CPU_CYCLES] = 0x003c, | 62 | [PERF_COUNT_CPU_CYCLES] = 0x003c, |
@@ -53,7 +68,10 @@ static const int intel_perfmon_event_map[] = | |||
53 | [PERF_COUNT_BUS_CYCLES] = 0x013c, | 68 | [PERF_COUNT_BUS_CYCLES] = 0x013c, |
54 | }; | 69 | }; |
55 | 70 | ||
56 | static const int max_intel_perfmon_events = ARRAY_SIZE(intel_perfmon_event_map); | 71 | static int pmc_intel_event_map(int event) |
72 | { | ||
73 | return intel_perfmon_event_map[event]; | ||
74 | } | ||
57 | 75 | ||
58 | /* | 76 | /* |
59 | * Propagate counter elapsed time into the generic counter. | 77 | * Propagate counter elapsed time into the generic counter. |
@@ -144,38 +162,48 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
144 | if (hw_event->raw) { | 162 | if (hw_event->raw) { |
145 | hwc->config |= hw_event->type; | 163 | hwc->config |= hw_event->type; |
146 | } else { | 164 | } else { |
147 | if (hw_event->type >= max_intel_perfmon_events) | 165 | if (hw_event->type >= pmc_ops->max_events) |
148 | return -EINVAL; | 166 | return -EINVAL; |
149 | /* | 167 | /* |
150 | * The generic map: | 168 | * The generic map: |
151 | */ | 169 | */ |
152 | hwc->config |= intel_perfmon_event_map[hw_event->type]; | 170 | hwc->config |= pmc_ops->event_map(hw_event->type); |
153 | } | 171 | } |
154 | counter->wakeup_pending = 0; | 172 | counter->wakeup_pending = 0; |
155 | 173 | ||
156 | return 0; | 174 | return 0; |
157 | } | 175 | } |
158 | 176 | ||
159 | u64 hw_perf_save_disable(void) | 177 | static u64 pmc_intel_save_disable_all(void) |
160 | { | 178 | { |
161 | u64 ctrl; | 179 | u64 ctrl; |
162 | 180 | ||
163 | if (unlikely(!perf_counters_initialized)) | ||
164 | return 0; | ||
165 | |||
166 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | 181 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); |
167 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); | 182 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
168 | 183 | ||
169 | return ctrl; | 184 | return ctrl; |
170 | } | 185 | } |
186 | |||
187 | u64 hw_perf_save_disable(void) | ||
188 | { | ||
189 | if (unlikely(!perf_counters_initialized)) | ||
190 | return 0; | ||
191 | |||
192 | return pmc_ops->save_disable_all(); | ||
193 | } | ||
171 | EXPORT_SYMBOL_GPL(hw_perf_save_disable); | 194 | EXPORT_SYMBOL_GPL(hw_perf_save_disable); |
172 | 195 | ||
196 | static void pmc_intel_restore_all(u64 ctrl) | ||
197 | { | ||
198 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | ||
199 | } | ||
200 | |||
173 | void hw_perf_restore(u64 ctrl) | 201 | void hw_perf_restore(u64 ctrl) |
174 | { | 202 | { |
175 | if (unlikely(!perf_counters_initialized)) | 203 | if (unlikely(!perf_counters_initialized)) |
176 | return; | 204 | return; |
177 | 205 | ||
178 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | 206 | pmc_ops->restore_all(ctrl); |
179 | } | 207 | } |
180 | EXPORT_SYMBOL_GPL(hw_perf_restore); | 208 | EXPORT_SYMBOL_GPL(hw_perf_restore); |
181 | 209 | ||
@@ -291,11 +319,11 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc) | |||
291 | 319 | ||
292 | event = hwc->config & ARCH_PERFMON_EVENT_MASK; | 320 | event = hwc->config & ARCH_PERFMON_EVENT_MASK; |
293 | 321 | ||
294 | if (unlikely(event == intel_perfmon_event_map[PERF_COUNT_INSTRUCTIONS])) | 322 | if (unlikely(event == pmc_ops->event_map(PERF_COUNT_INSTRUCTIONS))) |
295 | return X86_PMC_IDX_FIXED_INSTRUCTIONS; | 323 | return X86_PMC_IDX_FIXED_INSTRUCTIONS; |
296 | if (unlikely(event == intel_perfmon_event_map[PERF_COUNT_CPU_CYCLES])) | 324 | if (unlikely(event == pmc_ops->event_map(PERF_COUNT_CPU_CYCLES))) |
297 | return X86_PMC_IDX_FIXED_CPU_CYCLES; | 325 | return X86_PMC_IDX_FIXED_CPU_CYCLES; |
298 | if (unlikely(event == intel_perfmon_event_map[PERF_COUNT_BUS_CYCLES])) | 326 | if (unlikely(event == pmc_ops->event_map(PERF_COUNT_BUS_CYCLES))) |
299 | return X86_PMC_IDX_FIXED_BUS_CYCLES; | 327 | return X86_PMC_IDX_FIXED_BUS_CYCLES; |
300 | 328 | ||
301 | return -1; | 329 | return -1; |
@@ -339,8 +367,8 @@ try_generic: | |||
339 | set_bit(idx, cpuc->used); | 367 | set_bit(idx, cpuc->used); |
340 | hwc->idx = idx; | 368 | hwc->idx = idx; |
341 | } | 369 | } |
342 | hwc->config_base = MSR_ARCH_PERFMON_EVENTSEL0; | 370 | hwc->config_base = pmc_ops->eventsel; |
343 | hwc->counter_base = MSR_ARCH_PERFMON_PERFCTR0; | 371 | hwc->counter_base = pmc_ops->perfctr; |
344 | } | 372 | } |
345 | 373 | ||
346 | perf_counters_lapic_init(hwc->nmi); | 374 | perf_counters_lapic_init(hwc->nmi); |
@@ -386,8 +414,8 @@ void perf_counter_print_debug(void) | |||
386 | printk(KERN_INFO "CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used); | 414 | printk(KERN_INFO "CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used); |
387 | 415 | ||
388 | for (idx = 0; idx < nr_counters_generic; idx++) { | 416 | for (idx = 0; idx < nr_counters_generic; idx++) { |
389 | rdmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + idx, pmc_ctrl); | 417 | rdmsrl(pmc_ops->eventsel + idx, pmc_ctrl); |
390 | rdmsrl(MSR_ARCH_PERFMON_PERFCTR0 + idx, pmc_count); | 418 | rdmsrl(pmc_ops->perfctr + idx, pmc_count); |
391 | 419 | ||
392 | prev_left = per_cpu(prev_left[idx], cpu); | 420 | prev_left = per_cpu(prev_left[idx], cpu); |
393 | 421 | ||
@@ -655,29 +683,56 @@ static __read_mostly struct notifier_block perf_counter_nmi_notifier = { | |||
655 | .priority = 1 | 683 | .priority = 1 |
656 | }; | 684 | }; |
657 | 685 | ||
658 | void __init init_hw_perf_counters(void) | 686 | static struct pmc_x86_ops pmc_intel_ops = { |
687 | .save_disable_all = pmc_intel_save_disable_all, | ||
688 | .restore_all = pmc_intel_restore_all, | ||
689 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, | ||
690 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, | ||
691 | .event_map = pmc_intel_event_map, | ||
692 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), | ||
693 | }; | ||
694 | |||
695 | static struct pmc_x86_ops *pmc_intel_init(void) | ||
659 | { | 696 | { |
660 | union cpuid10_eax eax; | 697 | union cpuid10_eax eax; |
661 | unsigned int ebx; | 698 | unsigned int ebx; |
662 | unsigned int unused; | 699 | unsigned int unused; |
663 | union cpuid10_edx edx; | 700 | union cpuid10_edx edx; |
664 | 701 | ||
665 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | ||
666 | return; | ||
667 | |||
668 | /* | 702 | /* |
669 | * Check whether the Architectural PerfMon supports | 703 | * Check whether the Architectural PerfMon supports |
670 | * Branch Misses Retired Event or not. | 704 | * Branch Misses Retired Event or not. |
671 | */ | 705 | */ |
672 | cpuid(10, &eax.full, &ebx, &unused, &edx.full); | 706 | cpuid(10, &eax.full, &ebx, &unused, &edx.full); |
673 | if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) | 707 | if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) |
674 | return; | 708 | return NULL; |
675 | 709 | ||
676 | printk(KERN_INFO "Intel Performance Monitoring support detected.\n"); | 710 | printk(KERN_INFO "Intel Performance Monitoring support detected.\n"); |
677 | |||
678 | printk(KERN_INFO "... version: %d\n", eax.split.version_id); | 711 | printk(KERN_INFO "... version: %d\n", eax.split.version_id); |
679 | printk(KERN_INFO "... num counters: %d\n", eax.split.num_counters); | 712 | printk(KERN_INFO "... bit width: %d\n", eax.split.bit_width); |
713 | printk(KERN_INFO "... mask length: %d\n", eax.split.mask_length); | ||
714 | |||
680 | nr_counters_generic = eax.split.num_counters; | 715 | nr_counters_generic = eax.split.num_counters; |
716 | nr_counters_fixed = edx.split.num_counters_fixed; | ||
717 | counter_value_mask = (1ULL << eax.split.bit_width) - 1; | ||
718 | |||
719 | return &pmc_intel_ops; | ||
720 | } | ||
721 | |||
722 | void __init init_hw_perf_counters(void) | ||
723 | { | ||
724 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | ||
725 | return; | ||
726 | |||
727 | switch (boot_cpu_data.x86_vendor) { | ||
728 | case X86_VENDOR_INTEL: | ||
729 | pmc_ops = pmc_intel_init(); | ||
730 | break; | ||
731 | } | ||
732 | if (!pmc_ops) | ||
733 | return; | ||
734 | |||
735 | printk(KERN_INFO "... num counters: %d\n", nr_counters_generic); | ||
681 | if (nr_counters_generic > X86_PMC_MAX_GENERIC) { | 736 | if (nr_counters_generic > X86_PMC_MAX_GENERIC) { |
682 | nr_counters_generic = X86_PMC_MAX_GENERIC; | 737 | nr_counters_generic = X86_PMC_MAX_GENERIC; |
683 | WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", | 738 | WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", |
@@ -686,13 +741,8 @@ void __init init_hw_perf_counters(void) | |||
686 | perf_counter_mask = (1 << nr_counters_generic) - 1; | 741 | perf_counter_mask = (1 << nr_counters_generic) - 1; |
687 | perf_max_counters = nr_counters_generic; | 742 | perf_max_counters = nr_counters_generic; |
688 | 743 | ||
689 | printk(KERN_INFO "... bit width: %d\n", eax.split.bit_width); | ||
690 | counter_value_mask = (1ULL << eax.split.bit_width) - 1; | ||
691 | printk(KERN_INFO "... value mask: %016Lx\n", counter_value_mask); | 744 | printk(KERN_INFO "... value mask: %016Lx\n", counter_value_mask); |
692 | 745 | ||
693 | printk(KERN_INFO "... mask length: %d\n", eax.split.mask_length); | ||
694 | |||
695 | nr_counters_fixed = edx.split.num_counters_fixed; | ||
696 | if (nr_counters_fixed > X86_PMC_MAX_FIXED) { | 746 | if (nr_counters_fixed > X86_PMC_MAX_FIXED) { |
697 | nr_counters_fixed = X86_PMC_MAX_FIXED; | 747 | nr_counters_fixed = X86_PMC_MAX_FIXED; |
698 | WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", | 748 | WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", |