aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/ds.h12
-rw-r--r--arch/x86/kernel/ds.c69
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 {
234struct pebs_trace { 234struct 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 */
275extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value); 280extern 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
1200int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value) 1212int 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};
1217static const struct ds_configuration ds_cfg_pentium_m = { 1235static 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};
1221static const struct ds_configuration ds_cfg_core2_atom = { 1240static 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};
1247static 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
1228static void 1255static 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
1268void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) 1321void __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;