x86, ds: support Core i7

Add debug store support for Core i7.

Core i7 adds a reset value for each performance counter and a new
PEBS record format.

Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Cc: roland@redhat.com
Cc: eranian@googlemail.com
Cc: oleg@redhat.com
Cc: juan.villacis@intel.com
Cc: ak@linux.jf.intel.com
LKML-Reference: <20090403144607.088997000@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Markus Metzger 2009-04-03 16:43:52 +02:00 committed by Ingo Molnar
parent 150f5164c1
commit 017bc61765
2 changed files with 71 additions and 10 deletions

View File

@ -234,8 +234,12 @@ struct bts_trace {
struct pebs_trace { struct pebs_trace {
struct ds_trace ds; struct ds_trace ds;
/* the PEBS reset value */ /* the number of valid counters in the below array */
unsigned long long reset_value; unsigned int counters;
#define MAX_PEBS_COUNTERS 4
/* the counter reset value */
unsigned long long counter_reset[MAX_PEBS_COUNTERS];
}; };
@ -270,9 +274,11 @@ extern int ds_reset_pebs(struct pebs_tracer *tracer);
* Returns 0 on success; -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* tracer: the tracer handle returned from ds_request_pebs() * tracer: the tracer handle returned from ds_request_pebs()
* counter: the index of the counter
* value: the new counter reset value * value: the new counter reset value
*/ */
extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value); extern int ds_set_pebs_reset(struct pebs_tracer *tracer,
unsigned int counter, u64 value);
/* /*
* Initialization * Initialization

View File

@ -44,6 +44,9 @@ struct ds_configuration {
/* The size of a BTS/PEBS record in bytes: */ /* The size of a BTS/PEBS record in bytes: */
unsigned char sizeof_rec[2]; unsigned char sizeof_rec[2];
/* The number of pebs counter reset values in the DS structure. */
unsigned char nr_counter_reset;
/* Control bit-masks indexed by enum ds_feature: */ /* Control bit-masks indexed by enum ds_feature: */
unsigned long ctl[dsf_ctl_max]; unsigned long ctl[dsf_ctl_max];
}; };
@ -51,7 +54,7 @@ static struct ds_configuration ds_cfg __read_mostly;
/* Maximal size of a DS configuration: */ /* Maximal size of a DS configuration: */
#define MAX_SIZEOF_DS (12 * 8) #define MAX_SIZEOF_DS 0x80
/* Maximal size of a BTS record: */ /* Maximal size of a BTS record: */
#define MAX_SIZEOF_BTS (3 * 8) #define MAX_SIZEOF_BTS (3 * 8)
@ -59,6 +62,12 @@ static struct ds_configuration ds_cfg __read_mostly;
/* BTS and PEBS buffer alignment: */ /* BTS and PEBS buffer alignment: */
#define DS_ALIGNMENT (1 << 3) #define DS_ALIGNMENT (1 << 3)
/* Number of buffer pointers in DS: */
#define NUM_DS_PTR_FIELDS 8
/* Size of a pebs reset value in DS: */
#define PEBS_RESET_FIELD_SIZE 8
/* Mask of control bits in the DS MSR register: */ /* Mask of control bits in the DS MSR register: */
#define BTS_CONTROL \ #define BTS_CONTROL \
( ds_cfg.ctl[dsf_bts] | \ ( ds_cfg.ctl[dsf_bts] | \
@ -1164,9 +1173,12 @@ const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer)
return NULL; return NULL;
ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs);
tracer->trace.reset_value =
*(u64 *)(tracer->ds.context->ds + tracer->trace.counters = ds_cfg.nr_counter_reset;
(ds_cfg.sizeof_ptr_field * 8)); memcpy(tracer->trace.counter_reset,
tracer->ds.context->ds +
(NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field),
ds_cfg.nr_counter_reset * PEBS_RESET_FIELD_SIZE);
return &tracer->trace; return &tracer->trace;
} }
@ -1197,13 +1209,18 @@ int ds_reset_pebs(struct pebs_tracer *tracer)
return 0; return 0;
} }
int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value) int ds_set_pebs_reset(struct pebs_tracer *tracer,
unsigned int counter, u64 value)
{ {
if (!tracer) if (!tracer)
return -EINVAL; return -EINVAL;
if (ds_cfg.nr_counter_reset < counter)
return -EINVAL;
*(u64 *)(tracer->ds.context->ds + *(u64 *)(tracer->ds.context->ds +
(ds_cfg.sizeof_ptr_field * 8)) = value; (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field) +
(counter * PEBS_RESET_FIELD_SIZE)) = value;
return 0; return 0;
} }
@ -1213,16 +1230,26 @@ static const struct ds_configuration ds_cfg_netburst = {
.ctl[dsf_bts] = (1 << 2) | (1 << 3), .ctl[dsf_bts] = (1 << 2) | (1 << 3),
.ctl[dsf_bts_kernel] = (1 << 5), .ctl[dsf_bts_kernel] = (1 << 5),
.ctl[dsf_bts_user] = (1 << 6), .ctl[dsf_bts_user] = (1 << 6),
.nr_counter_reset = 1,
}; };
static const struct ds_configuration ds_cfg_pentium_m = { static const struct ds_configuration ds_cfg_pentium_m = {
.name = "Pentium M", .name = "Pentium M",
.ctl[dsf_bts] = (1 << 6) | (1 << 7), .ctl[dsf_bts] = (1 << 6) | (1 << 7),
.nr_counter_reset = 1,
}; };
static const struct ds_configuration ds_cfg_core2_atom = { static const struct ds_configuration ds_cfg_core2_atom = {
.name = "Core 2/Atom", .name = "Core 2/Atom",
.ctl[dsf_bts] = (1 << 6) | (1 << 7), .ctl[dsf_bts] = (1 << 6) | (1 << 7),
.ctl[dsf_bts_kernel] = (1 << 9), .ctl[dsf_bts_kernel] = (1 << 9),
.ctl[dsf_bts_user] = (1 << 10), .ctl[dsf_bts_user] = (1 << 10),
.nr_counter_reset = 1,
};
static const struct ds_configuration ds_cfg_core_i7 = {
.name = "Core i7",
.ctl[dsf_bts] = (1 << 6) | (1 << 7),
.ctl[dsf_bts_kernel] = (1 << 9),
.ctl[dsf_bts_user] = (1 << 10),
.nr_counter_reset = 4,
}; };
static void static void
@ -1239,6 +1266,32 @@ ds_configure(const struct ds_configuration *cfg,
nr_pebs_fields = 18; nr_pebs_fields = 18;
#endif #endif
/*
* Starting with version 2, architectural performance
* monitoring supports a format specifier.
*/
if ((cpuid_eax(0xa) & 0xff) > 1) {
unsigned long perf_capabilities, format;
rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_capabilities);
format = (perf_capabilities >> 8) & 0xf;
switch (format) {
case 0:
nr_pebs_fields = 18;
break;
case 1:
nr_pebs_fields = 22;
break;
default:
printk(KERN_INFO
"[ds] unknown PEBS format: %lu\n", format);
nr_pebs_fields = 0;
break;
}
}
memset(&ds_cfg, 0, sizeof(ds_cfg)); memset(&ds_cfg, 0, sizeof(ds_cfg));
ds_cfg = *cfg; ds_cfg = *cfg;
@ -1262,7 +1315,7 @@ ds_configure(const struct ds_configuration *cfg,
printk("bts/pebs record: %u/%u bytes\n", printk("bts/pebs record: %u/%u bytes\n",
ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]); ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]);
WARN_ON_ONCE(MAX_SIZEOF_DS < (12 * ds_cfg.sizeof_ptr_field)); WARN_ON_ONCE(MAX_PEBS_COUNTERS < ds_cfg.nr_counter_reset);
} }
void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
@ -1284,6 +1337,8 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
ds_configure(&ds_cfg_core2_atom, c); ds_configure(&ds_cfg_core2_atom, c);
break; break;
case 0x1a: /* Core i7 */ case 0x1a: /* Core i7 */
ds_configure(&ds_cfg_core_i7, c);
break;
default: default:
/* Sorry, don't know about them. */ /* Sorry, don't know about them. */
break; break;