@@ -8,4 +8,6 @@ void pmu_init(void)
cpuid_10 = cpuid(10);
if (this_cpu_has(X86_FEATURE_PDCM))
pmu.perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES);
+ pmu.msr_gp_counter_base = MSR_IA32_PERFCTR0;
+ pmu.msr_gp_event_select_base = MSR_P6_EVNTSEL0;
}
\ No newline at end of file
@@ -35,6 +35,8 @@
struct pmu_caps {
u64 perf_cap;
+ u32 msr_gp_counter_base;
+ u32 msr_gp_event_select_base;
};
extern struct cpuid cpuid_10;
@@ -42,6 +44,46 @@ extern struct pmu_caps pmu;
void pmu_init(void);
+static inline u32 gp_counter_base(void)
+{
+ return pmu.msr_gp_counter_base;
+}
+
+static inline void set_gp_counter_base(u32 new_base)
+{
+ pmu.msr_gp_counter_base = new_base;
+}
+
+static inline u32 gp_event_select_base(void)
+{
+ return pmu.msr_gp_event_select_base;
+}
+
+static inline void set_gp_event_select_base(u32 new_base)
+{
+ pmu.msr_gp_event_select_base = new_base;
+}
+
+static inline u32 gp_counter_msr(unsigned int i)
+{
+ return gp_counter_base() + i;
+}
+
+static inline u32 gp_event_select_msr(unsigned int i)
+{
+ return gp_event_select_base() + i;
+}
+
+static inline void write_gp_counter_value(unsigned int i, u64 value)
+{
+ wrmsr(gp_counter_msr(i), value);
+}
+
+static inline void write_gp_event_select(unsigned int i, u64 value)
+{
+ wrmsr(gp_event_select_msr(i), value);
+}
+
static inline u8 pmu_version(void)
{
return cpuid_10.a & 0xff;
@@ -109,4 +151,9 @@ static inline bool pmu_has_full_writes(void)
return this_cpu_perf_capabilities() & PMU_CAP_FW_WRITES;
}
+static inline bool pmu_use_full_writes(void)
+{
+ return gp_counter_base() == MSR_IA32_PMC0;
+}
+
#endif /* _X86_PMU_H_ */
@@ -44,8 +44,6 @@ struct pmu_event {
{"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
};
-static u64 gp_counter_base = MSR_IA32_PERFCTR0;
-
char *buf;
static inline void loop(void)
@@ -84,7 +82,7 @@ static bool is_gp(pmu_counter_t *evt)
static int event_to_global_idx(pmu_counter_t *cnt)
{
- return cnt->ctr - (is_gp(cnt) ? gp_counter_base :
+ return cnt->ctr - (is_gp(cnt) ? gp_counter_base() :
(MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
}
@@ -121,8 +119,7 @@ static void __start_event(pmu_counter_t *evt, uint64_t count)
evt->count = count;
wrmsr(evt->ctr, evt->count);
if (is_gp(evt))
- wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
- evt->config | EVNTSEL_EN);
+ write_gp_event_select(event_to_global_idx(evt), evt->config | EVNTSEL_EN);
else {
uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
@@ -150,8 +147,7 @@ static void stop_event(pmu_counter_t *evt)
{
global_disable(evt);
if (is_gp(evt))
- wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
- evt->config & ~EVNTSEL_EN);
+ write_gp_event_select(event_to_global_idx(evt), evt->config & ~EVNTSEL_EN);
else {
uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
@@ -198,12 +194,12 @@ static void check_gp_counter(struct pmu_event *evt)
{
int nr_gp_counters = pmu_nr_gp_counters();
pmu_counter_t cnt = {
- .ctr = gp_counter_base,
.config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
};
int i;
- for (i = 0; i < nr_gp_counters; i++, cnt.ctr++) {
+ for (i = 0; i < nr_gp_counters; i++) {
+ cnt.ctr = gp_counter_msr(i);
measure_one(&cnt);
report(verify_event(cnt.count, evt), "%s-%d", evt->name, i);
}
@@ -247,7 +243,7 @@ static void check_counters_many(void)
if (!pmu_gp_counter_is_available(i))
continue;
- cnt[n].ctr = gp_counter_base + n;
+ cnt[n].ctr = gp_counter_msr(n);
cnt[n].config = EVNTSEL_OS | EVNTSEL_USR |
gp_events[i % ARRAY_SIZE(gp_events)].unit_sel;
n++;
@@ -287,7 +283,7 @@ static void check_counter_overflow(void)
uint64_t overflow_preset;
int i;
pmu_counter_t cnt = {
- .ctr = gp_counter_base,
+ .ctr = gp_counter_msr(0),
.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
};
overflow_preset = measure_for_overflow(&cnt);
@@ -297,18 +293,20 @@ static void check_counter_overflow(void)
report_prefix_push("overflow");
- for (i = 0; i < nr_gp_counters + 1; i++, cnt.ctr++) {
+ for (i = 0; i < nr_gp_counters + 1; i++) {
uint64_t status;
int idx;
cnt.count = overflow_preset;
- if (gp_counter_base == MSR_IA32_PMC0)
+ if (pmu_use_full_writes())
cnt.count &= (1ull << pmu_gp_counter_width()) - 1;
if (i == nr_gp_counters) {
cnt.ctr = fixed_events[0].unit_sel;
cnt.count = measure_for_overflow(&cnt);
cnt.count &= (1ull << pmu_fixed_counter_width()) - 1;
+ } else {
+ cnt.ctr = gp_counter_msr(i);
}
if (i % 2)
@@ -332,7 +330,7 @@ static void check_counter_overflow(void)
static void check_gp_counter_cmask(void)
{
pmu_counter_t cnt = {
- .ctr = gp_counter_base,
+ .ctr = gp_counter_msr(0),
.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
};
cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
@@ -367,7 +365,7 @@ static void check_rdpmc(void)
for (i = 0; i < nr_gp_counters; i++) {
uint64_t x;
pmu_counter_t cnt = {
- .ctr = gp_counter_base + i,
+ .ctr = gp_counter_msr(i),
.idx = i
};
@@ -375,7 +373,7 @@ static void check_rdpmc(void)
* Without full-width writes, only the low 32 bits are writable,
* and the value is sign-extended.
*/
- if (gp_counter_base == MSR_IA32_PERFCTR0)
+ if (gp_counter_base() == MSR_IA32_PERFCTR0)
x = (uint64_t)(int64_t)(int32_t)val;
else
x = (uint64_t)(int64_t)val;
@@ -383,7 +381,7 @@ static void check_rdpmc(void)
/* Mask according to the number of supported bits */
x &= (1ull << gp_counter_width) - 1;
- wrmsr(gp_counter_base + i, val);
+ write_gp_counter_value(i, val);
report(rdpmc(i) == x, "cntr-%d", i);
exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt);
@@ -417,7 +415,7 @@ static void check_running_counter_wrmsr(void)
uint64_t status;
uint64_t count;
pmu_counter_t evt = {
- .ctr = gp_counter_base,
+ .ctr = gp_counter_msr(0),
.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
};
@@ -425,7 +423,7 @@ static void check_running_counter_wrmsr(void)
start_event(&evt);
loop();
- wrmsr(gp_counter_base, 0);
+ write_gp_counter_value(0, 0);
stop_event(&evt);
report(evt.count < gp_events[1].min, "cntr");
@@ -436,10 +434,10 @@ static void check_running_counter_wrmsr(void)
start_event(&evt);
count = -1;
- if (gp_counter_base == MSR_IA32_PMC0)
+ if (pmu_use_full_writes())
count &= (1ull << pmu_gp_counter_width()) - 1;
- wrmsr(gp_counter_base, count);
+ write_gp_counter_value(0, count);
loop();
stop_event(&evt);
@@ -453,12 +451,12 @@ static void check_emulated_instr(void)
{
uint64_t status, instr_start, brnch_start;
pmu_counter_t brnch_cnt = {
- .ctr = MSR_IA32_PERFCTR0,
+ .ctr = gp_counter_msr(0),
/* branch instructions */
.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[5].unit_sel,
};
pmu_counter_t instr_cnt = {
- .ctr = MSR_IA32_PERFCTR0 + 1,
+ .ctr = gp_counter_msr(1),
/* instructions */
.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
};
@@ -472,8 +470,8 @@ static void check_emulated_instr(void)
brnch_start = -EXPECTED_BRNCH;
instr_start = -EXPECTED_INSTR;
- wrmsr(MSR_IA32_PERFCTR0, brnch_start);
- wrmsr(MSR_IA32_PERFCTR0 + 1, instr_start);
+ write_gp_counter_value(0, brnch_start);
+ write_gp_counter_value(1, instr_start);
// KVM_FEP is a magic prefix that forces emulation so
// 'KVM_FEP "jne label\n"' just counts as a single instruction.
asm volatile(
@@ -670,7 +668,7 @@ int main(int ac, char **av)
check_counters();
if (pmu_has_full_writes()) {
- gp_counter_base = MSR_IA32_PMC0;
+ set_gp_counter_base(MSR_IA32_PMC0);
report_prefix_push("full-width writes");
check_counters();
check_gp_counters_write_width();