diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-05 14:22:46 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-06 07:14:47 -0400 |
commit | 8326f44da090d6d304d29b9fdc7fb3e20889e329 (patch) | |
tree | a15b2a2155c64a327b3cdf1da0997755d49390eb /arch/x86/kernel/cpu/perf_counter.c | |
parent | a21ca2cac582886a3e95c8bb84ff7c52d4d15e54 (diff) |
perf_counter: Implement generalized cache event types
Extend generic event enumeration with the PERF_TYPE_HW_CACHE
method.
This is a 3-dimensional space:
{ L1-D, L1-I, L2, ITLB, DTLB, BPU } x
{ load, store, prefetch } x
{ accesses, misses }
User-space passes in the 3 coordinates and the kernel provides
a counter. (if the hardware supports that type and if the
combination makes sense.)
Combinations that make no sense produce a -EINVAL.
Combinations that are not supported by the hardware produce -ENOTSUP.
Extend the tools to deal with this, and rewrite the event symbol
parsing code with various popular aliases for the units and
access methods above. So 'l1-cache-miss' and 'l1d-read-ops' are
both valid aliases.
( x86 is supported for now, with the Nehalem event table filled in,
and with Core2 and Atom having placeholder tables. )
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
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 | 201 |
1 files changed, 193 insertions, 8 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 430e048f2854..e86679fa5215 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -83,6 +83,128 @@ static u64 intel_pmu_event_map(int event) | |||
83 | return intel_perfmon_event_map[event]; | 83 | return intel_perfmon_event_map[event]; |
84 | } | 84 | } |
85 | 85 | ||
86 | /* | ||
87 | * Generalized hw caching related event table, filled | ||
88 | * in on a per model basis. A value of 0 means | ||
89 | * 'not supported', -1 means 'event makes no sense on | ||
90 | * this CPU', any other value means the raw event | ||
91 | * ID. | ||
92 | */ | ||
93 | |||
94 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
95 | |||
96 | static u64 __read_mostly hw_cache_event_ids | ||
97 | [PERF_COUNT_HW_CACHE_MAX] | ||
98 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
99 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; | ||
100 | |||
101 | static const u64 nehalem_hw_cache_event_ids | ||
102 | [PERF_COUNT_HW_CACHE_MAX] | ||
103 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
104 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | ||
105 | { | ||
106 | [ C(L1D) ] = { | ||
107 | [ C(OP_READ) ] = { | ||
108 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ | ||
109 | [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ | ||
110 | }, | ||
111 | [ C(OP_WRITE) ] = { | ||
112 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ | ||
113 | [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ | ||
114 | }, | ||
115 | [ C(OP_PREFETCH) ] = { | ||
116 | [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ | ||
117 | [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ | ||
118 | }, | ||
119 | }, | ||
120 | [ C(L1I ) ] = { | ||
121 | [ C(OP_READ) ] = { | ||
122 | [ C(RESULT_ACCESS) ] = 0x0480, /* L1I.READS */ | ||
123 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ | ||
124 | }, | ||
125 | [ C(OP_WRITE) ] = { | ||
126 | [ C(RESULT_ACCESS) ] = -1, | ||
127 | [ C(RESULT_MISS) ] = -1, | ||
128 | }, | ||
129 | [ C(OP_PREFETCH) ] = { | ||
130 | [ C(RESULT_ACCESS) ] = 0x0, | ||
131 | [ C(RESULT_MISS) ] = 0x0, | ||
132 | }, | ||
133 | }, | ||
134 | [ C(L2 ) ] = { | ||
135 | [ C(OP_READ) ] = { | ||
136 | [ C(RESULT_ACCESS) ] = 0x0324, /* L2_RQSTS.LOADS */ | ||
137 | [ C(RESULT_MISS) ] = 0x0224, /* L2_RQSTS.LD_MISS */ | ||
138 | }, | ||
139 | [ C(OP_WRITE) ] = { | ||
140 | [ C(RESULT_ACCESS) ] = 0x0c24, /* L2_RQSTS.RFOS */ | ||
141 | [ C(RESULT_MISS) ] = 0x0824, /* L2_RQSTS.RFO_MISS */ | ||
142 | }, | ||
143 | [ C(OP_PREFETCH) ] = { | ||
144 | [ C(RESULT_ACCESS) ] = 0xc024, /* L2_RQSTS.PREFETCHES */ | ||
145 | [ C(RESULT_MISS) ] = 0x8024, /* L2_RQSTS.PREFETCH_MISS */ | ||
146 | }, | ||
147 | }, | ||
148 | [ C(DTLB) ] = { | ||
149 | [ C(OP_READ) ] = { | ||
150 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ | ||
151 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ | ||
152 | }, | ||
153 | [ C(OP_WRITE) ] = { | ||
154 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ | ||
155 | [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ | ||
156 | }, | ||
157 | [ C(OP_PREFETCH) ] = { | ||
158 | [ C(RESULT_ACCESS) ] = 0x0, | ||
159 | [ C(RESULT_MISS) ] = 0x0, | ||
160 | }, | ||
161 | }, | ||
162 | [ C(ITLB) ] = { | ||
163 | [ C(OP_READ) ] = { | ||
164 | [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ | ||
165 | [ C(RESULT_MISS) ] = 0x0185, /* ITLB_MISS_RETIRED */ | ||
166 | }, | ||
167 | [ C(OP_WRITE) ] = { | ||
168 | [ C(RESULT_ACCESS) ] = -1, | ||
169 | [ C(RESULT_MISS) ] = -1, | ||
170 | }, | ||
171 | [ C(OP_PREFETCH) ] = { | ||
172 | [ C(RESULT_ACCESS) ] = -1, | ||
173 | [ C(RESULT_MISS) ] = -1, | ||
174 | }, | ||
175 | }, | ||
176 | [ C(BPU ) ] = { | ||
177 | [ C(OP_READ) ] = { | ||
178 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ | ||
179 | [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ | ||
180 | }, | ||
181 | [ C(OP_WRITE) ] = { | ||
182 | [ C(RESULT_ACCESS) ] = -1, | ||
183 | [ C(RESULT_MISS) ] = -1, | ||
184 | }, | ||
185 | [ C(OP_PREFETCH) ] = { | ||
186 | [ C(RESULT_ACCESS) ] = -1, | ||
187 | [ C(RESULT_MISS) ] = -1, | ||
188 | }, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | static const u64 core2_hw_cache_event_ids | ||
193 | [PERF_COUNT_HW_CACHE_MAX] | ||
194 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
195 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | ||
196 | { | ||
197 | /* To be filled in */ | ||
198 | }; | ||
199 | |||
200 | static const u64 atom_hw_cache_event_ids | ||
201 | [PERF_COUNT_HW_CACHE_MAX] | ||
202 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
203 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | ||
204 | { | ||
205 | /* To be filled in */ | ||
206 | }; | ||
207 | |||
86 | static u64 intel_pmu_raw_event(u64 event) | 208 | static u64 intel_pmu_raw_event(u64 event) |
87 | { | 209 | { |
88 | #define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL | 210 | #define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL |
@@ -246,6 +368,39 @@ static inline int x86_pmu_initialized(void) | |||
246 | return x86_pmu.handle_irq != NULL; | 368 | return x86_pmu.handle_irq != NULL; |
247 | } | 369 | } |
248 | 370 | ||
371 | static inline int | ||
372 | set_ext_hw_attr(struct hw_perf_counter *hwc, struct perf_counter_attr *attr) | ||
373 | { | ||
374 | unsigned int cache_type, cache_op, cache_result; | ||
375 | u64 config, val; | ||
376 | |||
377 | config = attr->config; | ||
378 | |||
379 | cache_type = (config >> 0) & 0xff; | ||
380 | if (cache_type >= PERF_COUNT_HW_CACHE_MAX) | ||
381 | return -EINVAL; | ||
382 | |||
383 | cache_op = (config >> 8) & 0xff; | ||
384 | if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) | ||
385 | return -EINVAL; | ||
386 | |||
387 | cache_result = (config >> 16) & 0xff; | ||
388 | if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) | ||
389 | return -EINVAL; | ||
390 | |||
391 | val = hw_cache_event_ids[cache_type][cache_op][cache_result]; | ||
392 | |||
393 | if (val == 0) | ||
394 | return -ENOENT; | ||
395 | |||
396 | if (val == -1) | ||
397 | return -EINVAL; | ||
398 | |||
399 | hwc->config |= val; | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
249 | /* | 404 | /* |
250 | * Setup the hardware configuration for a given attr_type | 405 | * Setup the hardware configuration for a given attr_type |
251 | */ | 406 | */ |
@@ -288,22 +443,25 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
288 | hwc->sample_period = x86_pmu.max_period; | 443 | hwc->sample_period = x86_pmu.max_period; |
289 | 444 | ||
290 | atomic64_set(&hwc->period_left, hwc->sample_period); | 445 | atomic64_set(&hwc->period_left, hwc->sample_period); |
446 | counter->destroy = hw_perf_counter_destroy; | ||
291 | 447 | ||
292 | /* | 448 | /* |
293 | * Raw event type provide the config in the event structure | 449 | * Raw event type provide the config in the event structure |
294 | */ | 450 | */ |
295 | if (attr->type == PERF_TYPE_RAW) { | 451 | if (attr->type == PERF_TYPE_RAW) { |
296 | hwc->config |= x86_pmu.raw_event(attr->config); | 452 | hwc->config |= x86_pmu.raw_event(attr->config); |
297 | } else { | 453 | return 0; |
298 | if (attr->config >= x86_pmu.max_events) | ||
299 | return -EINVAL; | ||
300 | /* | ||
301 | * The generic map: | ||
302 | */ | ||
303 | hwc->config |= x86_pmu.event_map(attr->config); | ||
304 | } | 454 | } |
305 | 455 | ||
306 | counter->destroy = hw_perf_counter_destroy; | 456 | if (attr->type == PERF_TYPE_HW_CACHE) |
457 | return set_ext_hw_attr(hwc, attr); | ||
458 | |||
459 | if (attr->config >= x86_pmu.max_events) | ||
460 | return -EINVAL; | ||
461 | /* | ||
462 | * The generic map: | ||
463 | */ | ||
464 | hwc->config |= x86_pmu.event_map(attr->config); | ||
307 | 465 | ||
308 | return 0; | 466 | return 0; |
309 | } | 467 | } |
@@ -989,6 +1147,33 @@ static int intel_pmu_init(void) | |||
989 | 1147 | ||
990 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); | 1148 | rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); |
991 | 1149 | ||
1150 | /* | ||
1151 | * Nehalem: | ||
1152 | */ | ||
1153 | switch (boot_cpu_data.x86_model) { | ||
1154 | case 17: | ||
1155 | memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, | ||
1156 | sizeof(u64)*PERF_COUNT_HW_CACHE_MAX* | ||
1157 | PERF_COUNT_HW_CACHE_OP_MAX*PERF_COUNT_HW_CACHE_RESULT_MAX); | ||
1158 | |||
1159 | pr_info("... installed Core2 event tables\n"); | ||
1160 | break; | ||
1161 | default: | ||
1162 | case 26: | ||
1163 | memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, | ||
1164 | sizeof(u64)*PERF_COUNT_HW_CACHE_MAX* | ||
1165 | PERF_COUNT_HW_CACHE_OP_MAX*PERF_COUNT_HW_CACHE_RESULT_MAX); | ||
1166 | |||
1167 | pr_info("... installed Nehalem/Corei7 event tables\n"); | ||
1168 | break; | ||
1169 | case 28: | ||
1170 | memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, | ||
1171 | sizeof(u64)*PERF_COUNT_HW_CACHE_MAX* | ||
1172 | PERF_COUNT_HW_CACHE_OP_MAX*PERF_COUNT_HW_CACHE_RESULT_MAX); | ||
1173 | |||
1174 | pr_info("... installed Atom event tables\n"); | ||
1175 | break; | ||
1176 | } | ||
992 | return 0; | 1177 | return 0; |
993 | } | 1178 | } |
994 | 1179 | ||