summaryrefslogtreecommitdiff
path: root/arch/x86/machine
diff options
context:
space:
mode:
authorRemy Noel <mocramis@gmail.com>2018-04-20 20:18:48 +0200
committerRemy Noel <mocramis@gmail.com>2018-04-21 00:04:20 +0200
commit0971db08230932057141990462f32c17ae088d82 (patch)
treee22734bb22239ed987e0624cd1556ed400e441d1 /arch/x86/machine
parent1b86674fcbefb0c860c02ade0463db469f4df1bb (diff)
pmu_intel: Add Overflow V2 support.
Also adds cpu_set_msr64 helper.
Diffstat (limited to 'arch/x86/machine')
-rw-r--r--arch/x86/machine/cpu.h13
-rw-r--r--arch/x86/machine/pmu_intel.c77
2 files changed, 83 insertions, 7 deletions
diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h
index d40760c..eebacc6 100644
--- a/arch/x86/machine/cpu.h
+++ b/arch/x86/machine/cpu.h
@@ -574,6 +574,19 @@ cpu_set_msr(uint32_t msr, uint32_t high, uint32_t low)
{
asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high) : "memory");
}
+/*
+ * uint64 version of cpu_set_msr.
+ */
+static inline void
+cpu_set_msr64(uint32_t msr, uint64_t value)
+{
+ uint32_t low, high;
+
+ low = value & 0xffffffff;
+ high = value >> 32;
+
+ cpu_set_msr(msr, high, low);
+}
static __always_inline uint64_t
cpu_get_tsc(void)
diff --git a/arch/x86/machine/pmu_intel.c b/arch/x86/machine/pmu_intel.c
index 40e1450..bc7405c 100644
--- a/arch/x86/machine/pmu_intel.c
+++ b/arch/x86/machine/pmu_intel.c
@@ -45,6 +45,17 @@
#define PMU_INTEL_MSR_EVTSEL0 0x186
/*
+ * V2 MSR addresses
+ */
+#define PMU_INTEL_MSR_FIXED_CTR0 0x0309
+#define PMU_INTEL_MSR_FIXED_CTR1 0x030a
+#define PMU_INTEL_MSR_FIXED_CTR2 0x030b
+#define PMU_INTEL_MSR_FIXED_CTR_CTRL 0x038d
+#define PMU_INTEL_MSR_GLOBAL_STATUS 0x038e
+#define PMU_INTEL_MSR_GLOBAL_CTRL 0x038f
+#define PMU_INTEL_MSR_GLOBAL_OVF_CTRL 0x0390
+
+/*
* Event Select Register addresses
*/
#define PMU_INTEL_EVTSEL_USR 0x00010000
@@ -117,6 +128,18 @@ pmu_intel_get(void)
return &pmu_intel;
}
+static uint64_t
+pmu_intel_get_status(void)
+{
+ return cpu_get_msr64(PMU_INTEL_MSR_GLOBAL_STATUS);
+}
+
+static void
+pmu_intel_ack_status(uint64_t status)
+{
+ return cpu_set_msr64(PMU_INTEL_MSR_GLOBAL_OVF_CTRL, status);
+}
+
static unsigned int
pmu_popcount(unsigned int bits)
{
@@ -236,12 +259,7 @@ pmu_intel_read(unsigned int pmc_id)
static void
pmu_intel_write(unsigned int pmc_id, uint64_t value)
{
- uint32_t low, high;
-
- low = value & 0xffffffff;
- high = value >> 32;
-
- cpu_set_msr(PMU_INTEL_MSR_PMC0 + pmc_id, low, high);
+ cpu_set_msr64(PMU_INTEL_MSR_PMC0 + pmc_id, value);
}
#endif /* CONFIG_PERFMON_TEST */
@@ -276,6 +294,47 @@ pmu_intel_handle_of_intr_v1(struct trap_frame *frame)
}
}
+static int
+pmu_intel_consume_bits(uint64_t *bits)
+{
+ int bit;
+
+ bit = __builtin_ffsll(*bits) - 1;
+ if (bit < 0) {
+ return bit;
+ }
+ *bits &= ~(1U << bit);
+
+ return bit;
+}
+
+static void
+pmu_intel_handle_of_intr_v2(struct trap_frame *frame)
+{
+ struct pmu_intel *pmu;
+ uint64_t status;
+ int pmc_id;
+
+ (void)frame;
+
+ status = pmu_intel_get_status();
+ if (status == 0) {
+ return;
+ }
+ pmu_intel_ack_status(status);
+ pmu = pmu_intel_get();
+
+ /* XXX: Mask on all PMCs (we do not check FIXED counters status */
+ status &= ((1U << pmu->pmc_width) - 1);
+ for(;;) {
+ pmc_id = pmu_intel_consume_bits(&status);
+ if (pmc_id < 0) {
+ break;
+ }
+ perfmon_cpu_pmc_inc_of(pmc_id);
+ }
+}
+
static uint8_t
pmu_intel_get_pmc_width(void)
{
@@ -332,7 +391,11 @@ pmu_intel_setup(void)
pmu_driver.stop = pmu_intel_stop;
pmu_driver.read = pmu_intel_read;
pmu_driver.get_pmc_width = pmu_intel_get_pmc_width;
- pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v1;
+ if (pmu->version > 2) {
+ pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v1;
+ } else {
+ pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v2;
+ }
#ifdef CONFIG_PERFMON_TEST
pmu_driver.write = pmu_intel_write;
#endif