diff options
-rw-r--r-- | arch/x86/include/asm/ds.h | 12 | ||||
-rw-r--r-- | arch/x86/kernel/ds.c | 69 |
2 files changed, 71 insertions, 10 deletions
diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h index 149e5208e967..70dac199b093 100644 --- a/arch/x86/include/asm/ds.h +++ b/arch/x86/include/asm/ds.h | |||
@@ -234,8 +234,12 @@ struct bts_trace { | |||
234 | struct pebs_trace { | 234 | struct pebs_trace { |
235 | struct ds_trace ds; | 235 | struct ds_trace ds; |
236 | 236 | ||
237 | /* the PEBS reset value */ | 237 | /* the number of valid counters in the below array */ |
238 | unsigned long long reset_value; | 238 | unsigned int counters; |
239 | |||
240 | #define MAX_PEBS_COUNTERS 4 | ||
241 | /* the counter reset value */ | ||
242 | unsigned long long counter_reset[MAX_PEBS_COUNTERS]; | ||
239 | }; | 243 | }; |
240 | 244 | ||
241 | 245 | ||
@@ -270,9 +274,11 @@ extern int ds_reset_pebs(struct pebs_tracer *tracer); | |||
270 | * Returns 0 on success; -Eerrno on error | 274 | * Returns 0 on success; -Eerrno on error |
271 | * | 275 | * |
272 | * tracer: the tracer handle returned from ds_request_pebs() | 276 | * tracer: the tracer handle returned from ds_request_pebs() |
277 | * counter: the index of the counter | ||
273 | * value: the new counter reset value | 278 | * value: the new counter reset value |
274 | */ | 279 | */ |
275 | extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value); | 280 | extern int ds_set_pebs_reset(struct pebs_tracer *tracer, |
281 | unsigned int counter, u64 value); | ||
276 | 282 | ||
277 | /* | 283 | /* |
278 | * Initialization | 284 | * Initialization |
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index 4e05157506aa..48bfe1386038 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c | |||
@@ -44,6 +44,9 @@ struct ds_configuration { | |||
44 | /* The size of a BTS/PEBS record in bytes: */ | 44 | /* The size of a BTS/PEBS record in bytes: */ |
45 | unsigned char sizeof_rec[2]; | 45 | unsigned char sizeof_rec[2]; |
46 | 46 | ||
47 | /* The number of pebs counter reset values in the DS structure. */ | ||
48 | unsigned char nr_counter_reset; | ||
49 | |||
47 | /* Control bit-masks indexed by enum ds_feature: */ | 50 | /* Control bit-masks indexed by enum ds_feature: */ |
48 | unsigned long ctl[dsf_ctl_max]; | 51 | unsigned long ctl[dsf_ctl_max]; |
49 | }; | 52 | }; |
@@ -51,7 +54,7 @@ static struct ds_configuration ds_cfg __read_mostly; | |||
51 | 54 | ||
52 | 55 | ||
53 | /* Maximal size of a DS configuration: */ | 56 | /* Maximal size of a DS configuration: */ |
54 | #define MAX_SIZEOF_DS (12 * 8) | 57 | #define MAX_SIZEOF_DS 0x80 |
55 | 58 | ||
56 | /* Maximal size of a BTS record: */ | 59 | /* Maximal size of a BTS record: */ |
57 | #define MAX_SIZEOF_BTS (3 * 8) | 60 | #define MAX_SIZEOF_BTS (3 * 8) |
@@ -59,6 +62,12 @@ static struct ds_configuration ds_cfg __read_mostly; | |||
59 | /* BTS and PEBS buffer alignment: */ | 62 | /* BTS and PEBS buffer alignment: */ |
60 | #define DS_ALIGNMENT (1 << 3) | 63 | #define DS_ALIGNMENT (1 << 3) |
61 | 64 | ||
65 | /* Number of buffer pointers in DS: */ | ||
66 | #define NUM_DS_PTR_FIELDS 8 | ||
67 | |||
68 | /* Size of a pebs reset value in DS: */ | ||
69 | #define PEBS_RESET_FIELD_SIZE 8 | ||
70 | |||
62 | /* Mask of control bits in the DS MSR register: */ | 71 | /* Mask of control bits in the DS MSR register: */ |
63 | #define BTS_CONTROL \ | 72 | #define BTS_CONTROL \ |
64 | ( ds_cfg.ctl[dsf_bts] | \ | 73 | ( ds_cfg.ctl[dsf_bts] | \ |
@@ -1164,9 +1173,12 @@ const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer) | |||
1164 | return NULL; | 1173 | return NULL; |
1165 | 1174 | ||
1166 | ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); | 1175 | ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); |
1167 | tracer->trace.reset_value = | 1176 | |
1168 | *(u64 *)(tracer->ds.context->ds + | 1177 | tracer->trace.counters = ds_cfg.nr_counter_reset; |
1169 | (ds_cfg.sizeof_ptr_field * 8)); | 1178 | memcpy(tracer->trace.counter_reset, |
1179 | tracer->ds.context->ds + | ||
1180 | (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field), | ||
1181 | ds_cfg.nr_counter_reset * PEBS_RESET_FIELD_SIZE); | ||
1170 | 1182 | ||
1171 | return &tracer->trace; | 1183 | return &tracer->trace; |
1172 | } | 1184 | } |
@@ -1197,13 +1209,18 @@ int ds_reset_pebs(struct pebs_tracer *tracer) | |||
1197 | return 0; | 1209 | return 0; |
1198 | } | 1210 | } |
1199 | 1211 | ||
1200 | int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value) | 1212 | int ds_set_pebs_reset(struct pebs_tracer *tracer, |
1213 | unsigned int counter, u64 value) | ||
1201 | { | 1214 | { |
1202 | if (!tracer) | 1215 | if (!tracer) |
1203 | return -EINVAL; | 1216 | return -EINVAL; |
1204 | 1217 | ||
1218 | if (ds_cfg.nr_counter_reset < counter) | ||
1219 | return -EINVAL; | ||
1220 | |||
1205 | *(u64 *)(tracer->ds.context->ds + | 1221 | *(u64 *)(tracer->ds.context->ds + |
1206 | (ds_cfg.sizeof_ptr_field * 8)) = value; | 1222 | (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field) + |
1223 | (counter * PEBS_RESET_FIELD_SIZE)) = value; | ||
1207 | 1224 | ||
1208 | return 0; | 1225 | return 0; |
1209 | } | 1226 | } |
@@ -1213,16 +1230,26 @@ static const struct ds_configuration ds_cfg_netburst = { | |||
1213 | .ctl[dsf_bts] = (1 << 2) | (1 << 3), | 1230 | .ctl[dsf_bts] = (1 << 2) | (1 << 3), |
1214 | .ctl[dsf_bts_kernel] = (1 << 5), | 1231 | .ctl[dsf_bts_kernel] = (1 << 5), |
1215 | .ctl[dsf_bts_user] = (1 << 6), | 1232 | .ctl[dsf_bts_user] = (1 << 6), |
1233 | .nr_counter_reset = 1, | ||
1216 | }; | 1234 | }; |
1217 | static const struct ds_configuration ds_cfg_pentium_m = { | 1235 | static const struct ds_configuration ds_cfg_pentium_m = { |
1218 | .name = "Pentium M", | 1236 | .name = "Pentium M", |
1219 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), | 1237 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), |
1238 | .nr_counter_reset = 1, | ||
1220 | }; | 1239 | }; |
1221 | static const struct ds_configuration ds_cfg_core2_atom = { | 1240 | static const struct ds_configuration ds_cfg_core2_atom = { |
1222 | .name = "Core 2/Atom", | 1241 | .name = "Core 2/Atom", |
1223 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), | 1242 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), |
1224 | .ctl[dsf_bts_kernel] = (1 << 9), | 1243 | .ctl[dsf_bts_kernel] = (1 << 9), |
1225 | .ctl[dsf_bts_user] = (1 << 10), | 1244 | .ctl[dsf_bts_user] = (1 << 10), |
1245 | .nr_counter_reset = 1, | ||
1246 | }; | ||
1247 | static const struct ds_configuration ds_cfg_core_i7 = { | ||
1248 | .name = "Core i7", | ||
1249 | .ctl[dsf_bts] = (1 << 6) | (1 << 7), | ||
1250 | .ctl[dsf_bts_kernel] = (1 << 9), | ||
1251 | .ctl[dsf_bts_user] = (1 << 10), | ||
1252 | .nr_counter_reset = 4, | ||
1226 | }; | 1253 | }; |
1227 | 1254 | ||
1228 | static void | 1255 | static void |
@@ -1239,6 +1266,32 @@ ds_configure(const struct ds_configuration *cfg, | |||
1239 | nr_pebs_fields = 18; | 1266 | nr_pebs_fields = 18; |
1240 | #endif | 1267 | #endif |
1241 | 1268 | ||
1269 | /* | ||
1270 | * Starting with version 2, architectural performance | ||
1271 | * monitoring supports a format specifier. | ||
1272 | */ | ||
1273 | if ((cpuid_eax(0xa) & 0xff) > 1) { | ||
1274 | unsigned long perf_capabilities, format; | ||
1275 | |||
1276 | rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_capabilities); | ||
1277 | |||
1278 | format = (perf_capabilities >> 8) & 0xf; | ||
1279 | |||
1280 | switch (format) { | ||
1281 | case 0: | ||
1282 | nr_pebs_fields = 18; | ||
1283 | break; | ||
1284 | case 1: | ||
1285 | nr_pebs_fields = 22; | ||
1286 | break; | ||
1287 | default: | ||
1288 | printk(KERN_INFO | ||
1289 | "[ds] unknown PEBS format: %lu\n", format); | ||
1290 | nr_pebs_fields = 0; | ||
1291 | break; | ||
1292 | } | ||
1293 | } | ||
1294 | |||
1242 | memset(&ds_cfg, 0, sizeof(ds_cfg)); | 1295 | memset(&ds_cfg, 0, sizeof(ds_cfg)); |
1243 | ds_cfg = *cfg; | 1296 | ds_cfg = *cfg; |
1244 | 1297 | ||
@@ -1262,7 +1315,7 @@ ds_configure(const struct ds_configuration *cfg, | |||
1262 | printk("bts/pebs record: %u/%u bytes\n", | 1315 | printk("bts/pebs record: %u/%u bytes\n", |
1263 | ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]); | 1316 | ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]); |
1264 | 1317 | ||
1265 | WARN_ON_ONCE(MAX_SIZEOF_DS < (12 * ds_cfg.sizeof_ptr_field)); | 1318 | WARN_ON_ONCE(MAX_PEBS_COUNTERS < ds_cfg.nr_counter_reset); |
1266 | } | 1319 | } |
1267 | 1320 | ||
1268 | void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) | 1321 | void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) |
@@ -1284,6 +1337,8 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) | |||
1284 | ds_configure(&ds_cfg_core2_atom, c); | 1337 | ds_configure(&ds_cfg_core2_atom, c); |
1285 | break; | 1338 | break; |
1286 | case 0x1a: /* Core i7 */ | 1339 | case 0x1a: /* Core i7 */ |
1340 | ds_configure(&ds_cfg_core_i7, c); | ||
1341 | break; | ||
1287 | default: | 1342 | default: |
1288 | /* Sorry, don't know about them. */ | 1343 | /* Sorry, don't know about them. */ |
1289 | break; | 1344 | break; |