diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-12-11 06:46:46 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-11 09:45:51 -0500 |
commit | 621a01eac89b5e2f81a4cf576568b31f40a02724 (patch) | |
tree | 79a7f2a8d49017077f40cfac4e78ee255cf2f43e | |
parent | ccff286d85098ba5438e22aa2ea807fc1e18cf2f (diff) |
perf counters: hw driver API
Impact: restructure code, introduce hw_ops driver abstraction
Introduce this abstraction to handle counter details:
struct hw_perf_counter_ops {
void (*hw_perf_counter_enable) (struct perf_counter *counter);
void (*hw_perf_counter_disable) (struct perf_counter *counter);
void (*hw_perf_counter_read) (struct perf_counter *counter);
};
This will be useful to support assymetric hw details, and it will also
be useful to implement "software counters". (Counters that count kernel
managed sw events such as pagefaults, context-switches, wall-clock time
or task-local time.)
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 37 | ||||
-rw-r--r-- | include/linux/perf_counter.h | 15 | ||||
-rw-r--r-- | kernel/perf_counter.c | 45 |
3 files changed, 66 insertions, 31 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 54b4ad0cce68..718b635dece6 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -56,7 +56,7 @@ const int max_intel_perfmon_events = ARRAY_SIZE(intel_perfmon_event_map); | |||
56 | /* | 56 | /* |
57 | * Setup the hardware configuration for a given hw_event_type | 57 | * Setup the hardware configuration for a given hw_event_type |
58 | */ | 58 | */ |
59 | int hw_perf_counter_init(struct perf_counter *counter) | 59 | static int __hw_perf_counter_init(struct perf_counter *counter) |
60 | { | 60 | { |
61 | struct perf_counter_hw_event *hw_event = &counter->hw_event; | 61 | struct perf_counter_hw_event *hw_event = &counter->hw_event; |
62 | struct hw_perf_counter *hwc = &counter->hw; | 62 | struct hw_perf_counter *hwc = &counter->hw; |
@@ -135,7 +135,7 @@ u64 hw_perf_disable_all(void) | |||
135 | EXPORT_SYMBOL_GPL(hw_perf_disable_all); | 135 | EXPORT_SYMBOL_GPL(hw_perf_disable_all); |
136 | 136 | ||
137 | static inline void | 137 | static inline void |
138 | __hw_perf_counter_disable(struct hw_perf_counter *hwc, unsigned int idx) | 138 | __x86_perf_counter_disable(struct hw_perf_counter *hwc, unsigned int idx) |
139 | { | 139 | { |
140 | wrmsr(hwc->config_base + idx, hwc->config, 0); | 140 | wrmsr(hwc->config_base + idx, hwc->config, 0); |
141 | } | 141 | } |
@@ -149,13 +149,13 @@ static void __hw_perf_counter_set_period(struct hw_perf_counter *hwc, int idx) | |||
149 | wrmsr(hwc->counter_base + idx, hwc->next_count, 0); | 149 | wrmsr(hwc->counter_base + idx, hwc->next_count, 0); |
150 | } | 150 | } |
151 | 151 | ||
152 | static void __hw_perf_counter_enable(struct hw_perf_counter *hwc, int idx) | 152 | static void __x86_perf_counter_enable(struct hw_perf_counter *hwc, int idx) |
153 | { | 153 | { |
154 | wrmsr(hwc->config_base + idx, | 154 | wrmsr(hwc->config_base + idx, |
155 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE, 0); | 155 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE, 0); |
156 | } | 156 | } |
157 | 157 | ||
158 | void hw_perf_counter_enable(struct perf_counter *counter) | 158 | static void x86_perf_counter_enable(struct perf_counter *counter) |
159 | { | 159 | { |
160 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | 160 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); |
161 | struct hw_perf_counter *hwc = &counter->hw; | 161 | struct hw_perf_counter *hwc = &counter->hw; |
@@ -170,12 +170,12 @@ void hw_perf_counter_enable(struct perf_counter *counter) | |||
170 | 170 | ||
171 | perf_counters_lapic_init(hwc->nmi); | 171 | perf_counters_lapic_init(hwc->nmi); |
172 | 172 | ||
173 | __hw_perf_counter_disable(hwc, idx); | 173 | __x86_perf_counter_disable(hwc, idx); |
174 | 174 | ||
175 | cpuc->counters[idx] = counter; | 175 | cpuc->counters[idx] = counter; |
176 | 176 | ||
177 | __hw_perf_counter_set_period(hwc, idx); | 177 | __hw_perf_counter_set_period(hwc, idx); |
178 | __hw_perf_counter_enable(hwc, idx); | 178 | __x86_perf_counter_enable(hwc, idx); |
179 | } | 179 | } |
180 | 180 | ||
181 | #ifdef CONFIG_X86_64 | 181 | #ifdef CONFIG_X86_64 |
@@ -282,20 +282,20 @@ void perf_counter_print_debug(void) | |||
282 | local_irq_enable(); | 282 | local_irq_enable(); |
283 | } | 283 | } |
284 | 284 | ||
285 | void hw_perf_counter_disable(struct perf_counter *counter) | 285 | static void x86_perf_counter_disable(struct perf_counter *counter) |
286 | { | 286 | { |
287 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | 287 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); |
288 | struct hw_perf_counter *hwc = &counter->hw; | 288 | struct hw_perf_counter *hwc = &counter->hw; |
289 | unsigned int idx = hwc->idx; | 289 | unsigned int idx = hwc->idx; |
290 | 290 | ||
291 | __hw_perf_counter_disable(hwc, idx); | 291 | __x86_perf_counter_disable(hwc, idx); |
292 | 292 | ||
293 | clear_bit(idx, cpuc->used); | 293 | clear_bit(idx, cpuc->used); |
294 | cpuc->counters[idx] = NULL; | 294 | cpuc->counters[idx] = NULL; |
295 | __hw_perf_save_counter(counter, hwc, idx); | 295 | __hw_perf_save_counter(counter, hwc, idx); |
296 | } | 296 | } |
297 | 297 | ||
298 | void hw_perf_counter_read(struct perf_counter *counter) | 298 | static void x86_perf_counter_read(struct perf_counter *counter) |
299 | { | 299 | { |
300 | struct hw_perf_counter *hwc = &counter->hw; | 300 | struct hw_perf_counter *hwc = &counter->hw; |
301 | unsigned long addr = hwc->counter_base + hwc->idx; | 301 | unsigned long addr = hwc->counter_base + hwc->idx; |
@@ -342,7 +342,7 @@ static void perf_save_and_restart(struct perf_counter *counter) | |||
342 | __hw_perf_counter_set_period(hwc, idx); | 342 | __hw_perf_counter_set_period(hwc, idx); |
343 | 343 | ||
344 | if (pmc_ctrl & ARCH_PERFMON_EVENTSEL0_ENABLE) | 344 | if (pmc_ctrl & ARCH_PERFMON_EVENTSEL0_ENABLE) |
345 | __hw_perf_counter_enable(hwc, idx); | 345 | __x86_perf_counter_enable(hwc, idx); |
346 | } | 346 | } |
347 | 347 | ||
348 | static void | 348 | static void |
@@ -572,3 +572,20 @@ void __init init_hw_perf_counters(void) | |||
572 | 572 | ||
573 | perf_counters_initialized = true; | 573 | perf_counters_initialized = true; |
574 | } | 574 | } |
575 | |||
576 | static struct hw_perf_counter_ops x86_perf_counter_ops = { | ||
577 | .hw_perf_counter_enable = x86_perf_counter_enable, | ||
578 | .hw_perf_counter_disable = x86_perf_counter_disable, | ||
579 | .hw_perf_counter_read = x86_perf_counter_read, | ||
580 | }; | ||
581 | |||
582 | struct hw_perf_counter_ops *hw_perf_counter_init(struct perf_counter *counter) | ||
583 | { | ||
584 | int err; | ||
585 | |||
586 | err = __hw_perf_counter_init(counter); | ||
587 | if (err) | ||
588 | return NULL; | ||
589 | |||
590 | return &x86_perf_counter_ops; | ||
591 | } | ||
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 7af7d8965460..27385641ecb6 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -113,6 +113,17 @@ struct perf_data { | |||
113 | u8 data[PERF_DATA_BUFLEN]; | 113 | u8 data[PERF_DATA_BUFLEN]; |
114 | }; | 114 | }; |
115 | 115 | ||
116 | struct perf_counter; | ||
117 | |||
118 | /** | ||
119 | * struct hw_perf_counter_ops - performance counter hw ops | ||
120 | */ | ||
121 | struct hw_perf_counter_ops { | ||
122 | void (*hw_perf_counter_enable) (struct perf_counter *counter); | ||
123 | void (*hw_perf_counter_disable) (struct perf_counter *counter); | ||
124 | void (*hw_perf_counter_read) (struct perf_counter *counter); | ||
125 | }; | ||
126 | |||
116 | /** | 127 | /** |
117 | * struct perf_counter - performance counter kernel representation: | 128 | * struct perf_counter - performance counter kernel representation: |
118 | */ | 129 | */ |
@@ -120,6 +131,7 @@ struct perf_counter { | |||
120 | struct list_head list_entry; | 131 | struct list_head list_entry; |
121 | struct list_head sibling_list; | 132 | struct list_head sibling_list; |
122 | struct perf_counter *group_leader; | 133 | struct perf_counter *group_leader; |
134 | struct hw_perf_counter_ops *hw_ops; | ||
123 | 135 | ||
124 | int active; | 136 | int active; |
125 | #if BITS_PER_LONG == 64 | 137 | #if BITS_PER_LONG == 64 |
@@ -185,6 +197,9 @@ struct perf_cpu_context { | |||
185 | extern int perf_max_counters; | 197 | extern int perf_max_counters; |
186 | 198 | ||
187 | #ifdef CONFIG_PERF_COUNTERS | 199 | #ifdef CONFIG_PERF_COUNTERS |
200 | extern struct hw_perf_counter_ops * | ||
201 | hw_perf_counter_init(struct perf_counter *counter); | ||
202 | |||
188 | extern void perf_counter_task_sched_in(struct task_struct *task, int cpu); | 203 | extern void perf_counter_task_sched_in(struct task_struct *task, int cpu); |
189 | extern void perf_counter_task_sched_out(struct task_struct *task, int cpu); | 204 | extern void perf_counter_task_sched_out(struct task_struct *task, int cpu); |
190 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); | 205 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 278209c547a8..e6e41ca95463 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -37,18 +37,15 @@ static DEFINE_MUTEX(perf_resource_mutex); | |||
37 | /* | 37 | /* |
38 | * Architecture provided APIs - weak aliases: | 38 | * Architecture provided APIs - weak aliases: |
39 | */ | 39 | */ |
40 | 40 | extern __weak struct hw_perf_counter_ops * | |
41 | int __weak hw_perf_counter_init(struct perf_counter *counter) | 41 | hw_perf_counter_init(struct perf_counter *counter) |
42 | { | 42 | { |
43 | return -EINVAL; | 43 | return ERR_PTR(-EINVAL); |
44 | } | 44 | } |
45 | 45 | ||
46 | void __weak hw_perf_counter_enable(struct perf_counter *counter) { } | 46 | void __weak hw_perf_disable_all(void) { } |
47 | void __weak hw_perf_counter_disable(struct perf_counter *counter) { } | 47 | void __weak hw_perf_enable_all(void) { } |
48 | void __weak hw_perf_counter_read(struct perf_counter *counter) { } | 48 | void __weak hw_perf_counter_setup(void) { } |
49 | void __weak hw_perf_disable_all(void) { } | ||
50 | void __weak hw_perf_enable_all(void) { } | ||
51 | void __weak hw_perf_counter_setup(void) { } | ||
52 | 49 | ||
53 | #if BITS_PER_LONG == 64 | 50 | #if BITS_PER_LONG == 64 |
54 | 51 | ||
@@ -146,7 +143,7 @@ static void __perf_counter_remove_from_context(void *info) | |||
146 | spin_lock(&ctx->lock); | 143 | spin_lock(&ctx->lock); |
147 | 144 | ||
148 | if (counter->active) { | 145 | if (counter->active) { |
149 | hw_perf_counter_disable(counter); | 146 | counter->hw_ops->hw_perf_counter_disable(counter); |
150 | counter->active = 0; | 147 | counter->active = 0; |
151 | ctx->nr_active--; | 148 | ctx->nr_active--; |
152 | cpuctx->active_oncpu--; | 149 | cpuctx->active_oncpu--; |
@@ -257,7 +254,7 @@ static void __perf_install_in_context(void *info) | |||
257 | ctx->nr_counters++; | 254 | ctx->nr_counters++; |
258 | 255 | ||
259 | if (cpuctx->active_oncpu < perf_max_counters) { | 256 | if (cpuctx->active_oncpu < perf_max_counters) { |
260 | hw_perf_counter_enable(counter); | 257 | counter->hw_ops->hw_perf_counter_enable(counter); |
261 | counter->active = 1; | 258 | counter->active = 1; |
262 | counter->oncpu = cpu; | 259 | counter->oncpu = cpu; |
263 | ctx->nr_active++; | 260 | ctx->nr_active++; |
@@ -333,7 +330,7 @@ counter_sched_out(struct perf_counter *counter, | |||
333 | if (!counter->active) | 330 | if (!counter->active) |
334 | return; | 331 | return; |
335 | 332 | ||
336 | hw_perf_counter_disable(counter); | 333 | counter->hw_ops->hw_perf_counter_disable(counter); |
337 | counter->active = 0; | 334 | counter->active = 0; |
338 | counter->oncpu = -1; | 335 | counter->oncpu = -1; |
339 | 336 | ||
@@ -392,7 +389,7 @@ counter_sched_in(struct perf_counter *counter, | |||
392 | struct perf_counter_context *ctx, | 389 | struct perf_counter_context *ctx, |
393 | int cpu) | 390 | int cpu) |
394 | { | 391 | { |
395 | hw_perf_counter_enable(counter); | 392 | counter->hw_ops->hw_perf_counter_enable(counter); |
396 | counter->active = 1; | 393 | counter->active = 1; |
397 | counter->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ | 394 | counter->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ |
398 | 395 | ||
@@ -509,7 +506,9 @@ void perf_counter_init_task(struct task_struct *task) | |||
509 | */ | 506 | */ |
510 | static void __hw_perf_counter_read(void *info) | 507 | static void __hw_perf_counter_read(void *info) |
511 | { | 508 | { |
512 | hw_perf_counter_read(info); | 509 | struct perf_counter *counter = info; |
510 | |||
511 | counter->hw_ops->hw_perf_counter_read(counter); | ||
513 | } | 512 | } |
514 | 513 | ||
515 | static u64 perf_counter_read(struct perf_counter *counter) | 514 | static u64 perf_counter_read(struct perf_counter *counter) |
@@ -816,8 +815,10 @@ perf_counter_alloc(struct perf_counter_hw_event *hw_event, | |||
816 | int cpu, | 815 | int cpu, |
817 | struct perf_counter *group_leader) | 816 | struct perf_counter *group_leader) |
818 | { | 817 | { |
819 | struct perf_counter *counter = kzalloc(sizeof(*counter), GFP_KERNEL); | 818 | struct hw_perf_counter_ops *hw_ops; |
819 | struct perf_counter *counter; | ||
820 | 820 | ||
821 | counter = kzalloc(sizeof(*counter), GFP_KERNEL); | ||
821 | if (!counter) | 822 | if (!counter) |
822 | return NULL; | 823 | return NULL; |
823 | 824 | ||
@@ -839,6 +840,14 @@ perf_counter_alloc(struct perf_counter_hw_event *hw_event, | |||
839 | counter->hw_event = *hw_event; | 840 | counter->hw_event = *hw_event; |
840 | counter->wakeup_pending = 0; | 841 | counter->wakeup_pending = 0; |
841 | counter->group_leader = group_leader; | 842 | counter->group_leader = group_leader; |
843 | counter->hw_ops = NULL; | ||
844 | |||
845 | hw_ops = hw_perf_counter_init(counter); | ||
846 | if (!hw_ops) { | ||
847 | kfree(counter); | ||
848 | return NULL; | ||
849 | } | ||
850 | counter->hw_ops = hw_ops; | ||
842 | 851 | ||
843 | return counter; | 852 | return counter; |
844 | } | 853 | } |
@@ -908,10 +917,6 @@ asmlinkage int sys_perf_counter_open( | |||
908 | if (!counter) | 917 | if (!counter) |
909 | goto err_put_context; | 918 | goto err_put_context; |
910 | 919 | ||
911 | ret = hw_perf_counter_init(counter); | ||
912 | if (ret) | ||
913 | goto err_free_put_context; | ||
914 | |||
915 | perf_install_in_context(ctx, counter, cpu); | 920 | perf_install_in_context(ctx, counter, cpu); |
916 | 921 | ||
917 | ret = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0); | 922 | ret = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0); |
@@ -927,8 +932,6 @@ err_remove_free_put_context: | |||
927 | mutex_lock(&counter->mutex); | 932 | mutex_lock(&counter->mutex); |
928 | perf_counter_remove_from_context(counter); | 933 | perf_counter_remove_from_context(counter); |
929 | mutex_unlock(&counter->mutex); | 934 | mutex_unlock(&counter->mutex); |
930 | |||
931 | err_free_put_context: | ||
932 | kfree(counter); | 935 | kfree(counter); |
933 | 936 | ||
934 | err_put_context: | 937 | err_put_context: |