aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-12-17 07:09:20 -0500
committerIngo Molnar <mingo@elte.hu>2008-12-23 06:45:12 -0500
commit862a1a5f346fe7e9181ea51eaae48cf2cd70f746 (patch)
tree6c38478c3197ae842a3fa8ebc1623bc8d3fde6a4
parent703e937c83bbad79075a7846e062e447c2fee6a4 (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.h14
-rw-r--r--arch/x86/kernel/cpu/perf_counter.c73
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
68extern void init_hw_perf_counters(void); 80extern void init_hw_perf_counters(void);
69extern void perf_counters_lapic_init(int nmi); 81extern 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 */
27static int nr_hw_counters __read_mostly; 27static int nr_counters_generic __read_mostly;
28static u32 perf_counter_mask __read_mostly; 28static u64 perf_counter_mask __read_mostly;
29 29
30static int nr_hw_counters_fixed __read_mostly; 30static int nr_counters_fixed __read_mostly;
31 31
32struct cpu_hw_counters { 32struct 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
165u64 hw_perf_save_disable(void) 162u64 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}
186EXPORT_SYMBOL_GPL(hw_perf_restore); 183EXPORT_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
239static 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
393again: 395again:
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
442void smp_perf_counter_interrupt(struct pt_regs *regs) 444void 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);