diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-12-17 07:09:20 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-23 06:45:12 -0500 |
commit | 862a1a5f346fe7e9181ea51eaae48cf2cd70f746 (patch) | |
tree | 6c38478c3197ae842a3fa8ebc1623bc8d3fde6a4 | |
parent | 703e937c83bbad79075a7846e062e447c2fee6a4 (diff) |
x86, perfcounters: refactor code for fixed-function PMCs
Impact: clean up
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/include/asm/perf_counter.h | 14 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 73 |
2 files changed, 52 insertions, 35 deletions
diff --git a/arch/x86/include/asm/perf_counter.h b/arch/x86/include/asm/perf_counter.h index 945a315e6d62..13745deb16c8 100644 --- a/arch/x86/include/asm/perf_counter.h +++ b/arch/x86/include/asm/perf_counter.h | |||
@@ -8,6 +8,10 @@ | |||
8 | #define X86_PMC_MAX_GENERIC 8 | 8 | #define X86_PMC_MAX_GENERIC 8 |
9 | #define X86_PMC_MAX_FIXED 3 | 9 | #define X86_PMC_MAX_FIXED 3 |
10 | 10 | ||
11 | #define X86_PMC_IDX_GENERIC 0 | ||
12 | #define X86_PMC_IDX_FIXED 32 | ||
13 | #define X86_PMC_IDX_MAX 64 | ||
14 | |||
11 | #define MSR_ARCH_PERFMON_PERFCTR0 0xc1 | 15 | #define MSR_ARCH_PERFMON_PERFCTR0 0xc1 |
12 | #define MSR_ARCH_PERFMON_PERFCTR1 0xc2 | 16 | #define MSR_ARCH_PERFMON_PERFCTR1 0xc2 |
13 | 17 | ||
@@ -54,6 +58,15 @@ union cpuid10_edx { | |||
54 | * Fixed-purpose performance counters: | 58 | * Fixed-purpose performance counters: |
55 | */ | 59 | */ |
56 | 60 | ||
61 | /* | ||
62 | * All 3 fixed-mode PMCs are configured via this single MSR: | ||
63 | */ | ||
64 | #define MSR_ARCH_PERFMON_FIXED_CTR_CTRL 0x38d | ||
65 | |||
66 | /* | ||
67 | * The counts are available in three separate MSRs: | ||
68 | */ | ||
69 | |||
57 | /* Instr_Retired.Any: */ | 70 | /* Instr_Retired.Any: */ |
58 | #define MSR_ARCH_PERFMON_FIXED_CTR0 0x309 | 71 | #define MSR_ARCH_PERFMON_FIXED_CTR0 0x309 |
59 | 72 | ||
@@ -63,7 +76,6 @@ union cpuid10_edx { | |||
63 | /* CPU_CLK_Unhalted.Ref: */ | 76 | /* CPU_CLK_Unhalted.Ref: */ |
64 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b | 77 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b |
65 | 78 | ||
66 | |||
67 | #ifdef CONFIG_PERF_COUNTERS | 79 | #ifdef CONFIG_PERF_COUNTERS |
68 | extern void init_hw_perf_counters(void); | 80 | extern void init_hw_perf_counters(void); |
69 | extern void perf_counters_lapic_init(int nmi); | 81 | extern void perf_counters_lapic_init(int nmi); |
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 2fca50c45979..358af5266407 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -24,17 +24,14 @@ static bool perf_counters_initialized __read_mostly; | |||
24 | /* | 24 | /* |
25 | * Number of (generic) HW counters: | 25 | * Number of (generic) HW counters: |
26 | */ | 26 | */ |
27 | static int nr_hw_counters __read_mostly; | 27 | static int nr_counters_generic __read_mostly; |
28 | static u32 perf_counter_mask __read_mostly; | 28 | static u64 perf_counter_mask __read_mostly; |
29 | 29 | ||
30 | static int nr_hw_counters_fixed __read_mostly; | 30 | static int nr_counters_fixed __read_mostly; |
31 | 31 | ||
32 | struct cpu_hw_counters { | 32 | struct cpu_hw_counters { |
33 | struct perf_counter *generic[X86_PMC_MAX_GENERIC]; | 33 | struct perf_counter *counters[X86_PMC_IDX_MAX]; |
34 | unsigned long used[BITS_TO_LONGS(X86_PMC_MAX_GENERIC)]; | 34 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
35 | |||
36 | struct perf_counter *fixed[X86_PMC_MAX_FIXED]; | ||
37 | unsigned long used_fixed[BITS_TO_LONGS(X86_PMC_MAX_FIXED)]; | ||
38 | }; | 35 | }; |
39 | 36 | ||
40 | /* | 37 | /* |
@@ -159,7 +156,7 @@ void hw_perf_enable_all(void) | |||
159 | if (unlikely(!perf_counters_initialized)) | 156 | if (unlikely(!perf_counters_initialized)) |
160 | return; | 157 | return; |
161 | 158 | ||
162 | wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, perf_counter_mask, 0); | 159 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, perf_counter_mask); |
163 | } | 160 | } |
164 | 161 | ||
165 | u64 hw_perf_save_disable(void) | 162 | u64 hw_perf_save_disable(void) |
@@ -170,7 +167,7 @@ u64 hw_perf_save_disable(void) | |||
170 | return 0; | 167 | return 0; |
171 | 168 | ||
172 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); | 169 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); |
173 | wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0, 0); | 170 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
174 | 171 | ||
175 | return ctrl; | 172 | return ctrl; |
176 | } | 173 | } |
@@ -181,7 +178,7 @@ void hw_perf_restore(u64 ctrl) | |||
181 | if (unlikely(!perf_counters_initialized)) | 178 | if (unlikely(!perf_counters_initialized)) |
182 | return; | 179 | return; |
183 | 180 | ||
184 | wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, ctrl, 0); | 181 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); |
185 | } | 182 | } |
186 | EXPORT_SYMBOL_GPL(hw_perf_restore); | 183 | EXPORT_SYMBOL_GPL(hw_perf_restore); |
187 | 184 | ||
@@ -239,6 +236,11 @@ __pmc_generic_enable(struct perf_counter *counter, | |||
239 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE, 0); | 236 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE, 0); |
240 | } | 237 | } |
241 | 238 | ||
239 | static int fixed_mode_idx(struct hw_perf_counter *hwc) | ||
240 | { | ||
241 | return -1; | ||
242 | } | ||
243 | |||
242 | /* | 244 | /* |
243 | * Find a PMC slot for the freshly enabled / scheduled in counter: | 245 | * Find a PMC slot for the freshly enabled / scheduled in counter: |
244 | */ | 246 | */ |
@@ -250,7 +252,7 @@ static void pmc_generic_enable(struct perf_counter *counter) | |||
250 | 252 | ||
251 | /* Try to get the previous counter again */ | 253 | /* Try to get the previous counter again */ |
252 | if (test_and_set_bit(idx, cpuc->used)) { | 254 | if (test_and_set_bit(idx, cpuc->used)) { |
253 | idx = find_first_zero_bit(cpuc->used, nr_hw_counters); | 255 | idx = find_first_zero_bit(cpuc->used, nr_counters_generic); |
254 | set_bit(idx, cpuc->used); | 256 | set_bit(idx, cpuc->used); |
255 | hwc->idx = idx; | 257 | hwc->idx = idx; |
256 | } | 258 | } |
@@ -259,7 +261,7 @@ static void pmc_generic_enable(struct perf_counter *counter) | |||
259 | 261 | ||
260 | __pmc_generic_disable(counter, hwc, idx); | 262 | __pmc_generic_disable(counter, hwc, idx); |
261 | 263 | ||
262 | cpuc->generic[idx] = counter; | 264 | cpuc->counters[idx] = counter; |
263 | 265 | ||
264 | __hw_perf_counter_set_period(counter, hwc, idx); | 266 | __hw_perf_counter_set_period(counter, hwc, idx); |
265 | __pmc_generic_enable(counter, hwc, idx); | 267 | __pmc_generic_enable(counter, hwc, idx); |
@@ -270,7 +272,7 @@ void perf_counter_print_debug(void) | |||
270 | u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left; | 272 | u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left; |
271 | int cpu, idx; | 273 | int cpu, idx; |
272 | 274 | ||
273 | if (!nr_hw_counters) | 275 | if (!nr_counters_generic) |
274 | return; | 276 | return; |
275 | 277 | ||
276 | local_irq_disable(); | 278 | local_irq_disable(); |
@@ -286,7 +288,7 @@ void perf_counter_print_debug(void) | |||
286 | printk(KERN_INFO "CPU#%d: status: %016llx\n", cpu, status); | 288 | printk(KERN_INFO "CPU#%d: status: %016llx\n", cpu, status); |
287 | printk(KERN_INFO "CPU#%d: overflow: %016llx\n", cpu, overflow); | 289 | printk(KERN_INFO "CPU#%d: overflow: %016llx\n", cpu, overflow); |
288 | 290 | ||
289 | for (idx = 0; idx < nr_hw_counters; idx++) { | 291 | for (idx = 0; idx < nr_counters_generic; idx++) { |
290 | rdmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + idx, pmc_ctrl); | 292 | rdmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + idx, pmc_ctrl); |
291 | rdmsrl(MSR_ARCH_PERFMON_PERFCTR0 + idx, pmc_count); | 293 | rdmsrl(MSR_ARCH_PERFMON_PERFCTR0 + idx, pmc_count); |
292 | 294 | ||
@@ -311,7 +313,7 @@ static void pmc_generic_disable(struct perf_counter *counter) | |||
311 | __pmc_generic_disable(counter, hwc, idx); | 313 | __pmc_generic_disable(counter, hwc, idx); |
312 | 314 | ||
313 | clear_bit(idx, cpuc->used); | 315 | clear_bit(idx, cpuc->used); |
314 | cpuc->generic[idx] = NULL; | 316 | cpuc->counters[idx] = NULL; |
315 | 317 | ||
316 | /* | 318 | /* |
317 | * Drain the remaining delta count out of a counter | 319 | * Drain the remaining delta count out of a counter |
@@ -381,7 +383,7 @@ static void __smp_perf_counter_interrupt(struct pt_regs *regs, int nmi) | |||
381 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, saved_global); | 383 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, saved_global); |
382 | 384 | ||
383 | /* Disable counters globally */ | 385 | /* Disable counters globally */ |
384 | wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0, 0); | 386 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
385 | ack_APIC_irq(); | 387 | ack_APIC_irq(); |
386 | 388 | ||
387 | cpuc = &per_cpu(cpu_hw_counters, cpu); | 389 | cpuc = &per_cpu(cpu_hw_counters, cpu); |
@@ -392,8 +394,8 @@ static void __smp_perf_counter_interrupt(struct pt_regs *regs, int nmi) | |||
392 | 394 | ||
393 | again: | 395 | again: |
394 | ack = status; | 396 | ack = status; |
395 | for_each_bit(bit, (unsigned long *) &status, nr_hw_counters) { | 397 | for_each_bit(bit, (unsigned long *) &status, nr_counters_generic) { |
396 | struct perf_counter *counter = cpuc->generic[bit]; | 398 | struct perf_counter *counter = cpuc->counters[bit]; |
397 | 399 | ||
398 | clear_bit(bit, (unsigned long *) &status); | 400 | clear_bit(bit, (unsigned long *) &status); |
399 | if (!counter) | 401 | if (!counter) |
@@ -424,7 +426,7 @@ again: | |||
424 | } | 426 | } |
425 | } | 427 | } |
426 | 428 | ||
427 | wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack, 0); | 429 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); |
428 | 430 | ||
429 | /* | 431 | /* |
430 | * Repeat if there is more work to be done: | 432 | * Repeat if there is more work to be done: |
@@ -436,7 +438,7 @@ out: | |||
436 | /* | 438 | /* |
437 | * Restore - do not reenable when global enable is off: | 439 | * Restore - do not reenable when global enable is off: |
438 | */ | 440 | */ |
439 | wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, saved_global, 0); | 441 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, saved_global); |
440 | } | 442 | } |
441 | 443 | ||
442 | void smp_perf_counter_interrupt(struct pt_regs *regs) | 444 | void smp_perf_counter_interrupt(struct pt_regs *regs) |
@@ -462,8 +464,8 @@ void perf_counter_notify(struct pt_regs *regs) | |||
462 | cpu = smp_processor_id(); | 464 | cpu = smp_processor_id(); |
463 | cpuc = &per_cpu(cpu_hw_counters, cpu); | 465 | cpuc = &per_cpu(cpu_hw_counters, cpu); |
464 | 466 | ||
465 | for_each_bit(bit, cpuc->used, nr_hw_counters) { | 467 | for_each_bit(bit, cpuc->used, X86_PMC_IDX_MAX) { |
466 | struct perf_counter *counter = cpuc->generic[bit]; | 468 | struct perf_counter *counter = cpuc->counters[bit]; |
467 | 469 | ||
468 | if (!counter) | 470 | if (!counter) |
469 | continue; | 471 | continue; |
@@ -540,26 +542,29 @@ void __init init_hw_perf_counters(void) | |||
540 | 542 | ||
541 | printk(KERN_INFO "... version: %d\n", eax.split.version_id); | 543 | printk(KERN_INFO "... version: %d\n", eax.split.version_id); |
542 | printk(KERN_INFO "... num counters: %d\n", eax.split.num_counters); | 544 | printk(KERN_INFO "... num counters: %d\n", eax.split.num_counters); |
543 | nr_hw_counters = eax.split.num_counters; | 545 | nr_counters_generic = eax.split.num_counters; |
544 | if (nr_hw_counters > X86_PMC_MAX_GENERIC) { | 546 | if (nr_counters_generic > X86_PMC_MAX_GENERIC) { |
545 | nr_hw_counters = X86_PMC_MAX_GENERIC; | 547 | nr_counters_generic = X86_PMC_MAX_GENERIC; |
546 | WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", | 548 | WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", |
547 | nr_hw_counters, X86_PMC_MAX_GENERIC); | 549 | nr_counters_generic, X86_PMC_MAX_GENERIC); |
548 | } | 550 | } |
549 | perf_counter_mask = (1 << nr_hw_counters) - 1; | 551 | perf_counter_mask = (1 << nr_counters_generic) - 1; |
550 | perf_max_counters = nr_hw_counters; | 552 | perf_max_counters = nr_counters_generic; |
551 | 553 | ||
552 | printk(KERN_INFO "... bit width: %d\n", eax.split.bit_width); | 554 | printk(KERN_INFO "... bit width: %d\n", eax.split.bit_width); |
553 | printk(KERN_INFO "... mask length: %d\n", eax.split.mask_length); | 555 | printk(KERN_INFO "... mask length: %d\n", eax.split.mask_length); |
554 | 556 | ||
555 | nr_hw_counters_fixed = edx.split.num_counters_fixed; | 557 | nr_counters_fixed = edx.split.num_counters_fixed; |
556 | if (nr_hw_counters_fixed > X86_PMC_MAX_FIXED) { | 558 | if (nr_counters_fixed > X86_PMC_MAX_FIXED) { |
557 | nr_hw_counters_fixed = X86_PMC_MAX_FIXED; | 559 | nr_counters_fixed = X86_PMC_MAX_FIXED; |
558 | WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", | 560 | WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", |
559 | nr_hw_counters_fixed, X86_PMC_MAX_FIXED); | 561 | nr_counters_fixed, X86_PMC_MAX_FIXED); |
560 | } | 562 | } |
561 | printk(KERN_INFO "... fixed counters: %d\n", nr_hw_counters_fixed); | 563 | printk(KERN_INFO "... fixed counters: %d\n", nr_counters_fixed); |
564 | |||
565 | perf_counter_mask |= ((1LL << nr_counters_fixed)-1) << X86_PMC_IDX_FIXED; | ||
562 | 566 | ||
567 | printk(KERN_INFO "... counter mask: %016Lx\n", perf_counter_mask); | ||
563 | perf_counters_initialized = true; | 568 | perf_counters_initialized = true; |
564 | 569 | ||
565 | perf_counters_lapic_init(0); | 570 | perf_counters_lapic_init(0); |