* [kvm-unit-tests RFC 0/3] x86/pmu: Add AMD vPMU testing code
@ 2017-08-04 4:24 Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h Wei Huang
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Wei Huang @ 2017-08-04 4:24 UTC (permalink / raw)
To: kvm; +Cc: pbonzini, rkrcmar, wei
This patchset adds the PMU testing case for AMD CPU. The original pmu
test code is re-named to intel_pmu. And common PMU event and counter
definitions are moved to pmu.h, which is shared between intel_pmu and
amd_pmc.
Thanks,
-Wei
Wei Huang (3):
pmu: move common x86 PMU definitions to pmu.h
pmu: Rename pmu.c code to intel_pmu.c
pmu: Create AMD PMU test code
x86/Makefile.common | 3 +-
x86/amd_pmu.c | 265 +++++++++++++++++++++++++++++++++
x86/intel_pmu.c | 379 +++++++++++++++++++++++++++++++++++++++++++++++
x86/pmu.c | 413 ----------------------------------------------------
x86/pmu.h | 42 ++++++
x86/unittests.cfg | 8 +-
6 files changed, 694 insertions(+), 416 deletions(-)
create mode 100644 x86/amd_pmu.c
create mode 100644 x86/intel_pmu.c
delete mode 100644 x86/pmu.c
create mode 100644 x86/pmu.h
--
2.7.5
^ permalink raw reply [flat|nested] 9+ messages in thread
* [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h
2017-08-04 4:24 [kvm-unit-tests RFC 0/3] x86/pmu: Add AMD vPMU testing code Wei Huang
@ 2017-08-04 4:24 ` Wei Huang
2017-08-04 7:59 ` Andrew Jones
2017-08-04 4:24 ` [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code Wei Huang
2 siblings, 1 reply; 9+ messages in thread
From: Wei Huang @ 2017-08-04 4:24 UTC (permalink / raw)
To: kvm; +Cc: pbonzini, rkrcmar, wei
This patch moves common x86 PMU counter and event defintions to pmu.h
file. This header file will be used by both Intel and AMD PMU test
code.
Signed-off-by: Wei Huang <wei@redhat.com>
---
x86/pmu.c | 38 ++------------------------------------
x86/pmu.h | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 36 deletions(-)
create mode 100644 x86/pmu.h
diff --git a/x86/pmu.c b/x86/pmu.c
index a0238dc..1b818c2 100644
--- a/x86/pmu.c
+++ b/x86/pmu.c
@@ -8,38 +8,9 @@
#include "x86/vm.h"
#include "libcflat.h"
+#include "pmu.h"
#include <stdint.h>
-#define FIXED_CNT_INDEX 32
-#define PC_VECTOR 32
-
-#define EVNSEL_EVENT_SHIFT 0
-#define EVNTSEL_UMASK_SHIFT 8
-#define EVNTSEL_USR_SHIFT 16
-#define EVNTSEL_OS_SHIFT 17
-#define EVNTSEL_EDGE_SHIFT 18
-#define EVNTSEL_PC_SHIFT 19
-#define EVNTSEL_INT_SHIFT 20
-#define EVNTSEL_EN_SHIF 22
-#define EVNTSEL_INV_SHIF 23
-#define EVNTSEL_CMASK_SHIFT 24
-
-#define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF)
-#define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT)
-#define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT)
-#define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT)
-#define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT)
-#define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF)
-
-#define N 1000000
-
-typedef struct {
- uint32_t ctr;
- uint32_t config;
- uint64_t count;
- int idx;
-} pmu_counter_t;
-
union cpuid10_eax {
struct {
unsigned int version_id:8;
@@ -72,12 +43,7 @@ union cpuid10_edx {
unsigned int full;
} edx;
-struct pmu_event {
- const char *name;
- uint32_t unit_sel;
- int min;
- int max;
-} gp_events[] = {
+struct pmu_event gp_events[] = {
{"core cycles", 0x003c, 1*N, 50*N},
{"instructions", 0x00c0, 10*N, 10.2*N},
{"ref cycles", 0x013c, 0.1*N, 30*N},
diff --git a/x86/pmu.h b/x86/pmu.h
new file mode 100644
index 0000000..b1e8e51
--- /dev/null
+++ b/x86/pmu.h
@@ -0,0 +1,42 @@
+/* vPMU unit testing for x86 */
+#ifndef __PMU_H
+#define __PMU_H
+
+#define N 1000000
+
+#define FIXED_CNT_INDEX 32
+#define PC_VECTOR 32
+
+#define EVNTSEL_EVENT_SHIFT 0
+#define EVNTSEL_UMASK_SHIFT 8
+#define EVNTSEL_USR_SHIFT 16
+#define EVNTSEL_OS_SHIFT 17
+#define EVNTSEL_EDGE_SHIFT 18
+#define EVNTSEL_PC_SHIFT 19
+#define EVNTSEL_INT_SHIFT 20
+#define EVNTSEL_EN_SHIF 22
+#define EVNTSEL_INV_SHIF 23
+#define EVNTSEL_CMASK_SHIFT 24
+
+#define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF)
+#define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT)
+#define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT)
+#define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT)
+#define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT)
+#define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF)
+
+typedef struct {
+ uint32_t ctr;
+ uint32_t config;
+ uint64_t count;
+ int idx;
+} pmu_counter_t;
+
+struct pmu_event {
+ const char *name;
+ uint32_t unit_sel;
+ int min;
+ int max;
+};
+
+#endif /* __PMU_H */
--
2.7.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c
2017-08-04 4:24 [kvm-unit-tests RFC 0/3] x86/pmu: Add AMD vPMU testing code Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h Wei Huang
@ 2017-08-04 4:24 ` Wei Huang
2017-08-04 8:00 ` Andrew Jones
2017-08-04 4:24 ` [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code Wei Huang
2 siblings, 1 reply; 9+ messages in thread
From: Wei Huang @ 2017-08-04 4:24 UTC (permalink / raw)
To: kvm; +Cc: pbonzini, rkrcmar, wei
Many code in pmu.c is Intel-specific (e.g. fixed counters, global
ctrl). This patch renames pmu.c to intel_pmu.c. The makefile and
unit-test config file are also changed acccordingly.
Signed-off-by: Wei Huang <wei@redhat.com>
---
x86/Makefile.common | 2 +-
x86/intel_pmu.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++
x86/pmu.c | 379 ----------------------------------------------------
x86/unittests.cfg | 4 +-
4 files changed, 382 insertions(+), 382 deletions(-)
create mode 100644 x86/intel_pmu.c
delete mode 100644 x86/pmu.c
diff --git a/x86/Makefile.common b/x86/Makefile.common
index e96812b..d0f4ed1 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -45,7 +45,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
$(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
$(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
$(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \
- $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat $(TEST_DIR)/setjmp.flat \
+ $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat $(TEST_DIR)/setjmp.flat \
$(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
$(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
$(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
diff --git a/x86/intel_pmu.c b/x86/intel_pmu.c
new file mode 100644
index 0000000..1b818c2
--- /dev/null
+++ b/x86/intel_pmu.c
@@ -0,0 +1,379 @@
+
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include "pmu.h"
+#include <stdint.h>
+
+union cpuid10_eax {
+ struct {
+ unsigned int version_id:8;
+ unsigned int num_counters:8;
+ unsigned int bit_width:8;
+ unsigned int mask_length:8;
+ } split;
+ unsigned int full;
+} eax;
+
+union cpuid10_ebx {
+ struct {
+ unsigned int no_unhalted_core_cycles:1;
+ unsigned int no_instructions_retired:1;
+ unsigned int no_unhalted_reference_cycles:1;
+ unsigned int no_llc_reference:1;
+ unsigned int no_llc_misses:1;
+ unsigned int no_branch_instruction_retired:1;
+ unsigned int no_branch_misses_retired:1;
+ } split;
+ unsigned int full;
+} ebx;
+
+union cpuid10_edx {
+ struct {
+ unsigned int num_counters_fixed:5;
+ unsigned int bit_width_fixed:8;
+ unsigned int reserved:19;
+ } split;
+ unsigned int full;
+} edx;
+
+struct pmu_event gp_events[] = {
+ {"core cycles", 0x003c, 1*N, 50*N},
+ {"instructions", 0x00c0, 10*N, 10.2*N},
+ {"ref cycles", 0x013c, 0.1*N, 30*N},
+ {"llc refference", 0x4f2e, 1, 2*N},
+ {"llc misses", 0x412e, 1, 1*N},
+ {"branches", 0x00c4, 1*N, 1.1*N},
+ {"branch misses", 0x00c5, 0, 0.1*N},
+}, fixed_events[] = {
+ {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N},
+ {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N},
+ {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
+};
+
+static int num_counters;
+
+char *buf;
+
+static inline void loop()
+{
+ unsigned long tmp, tmp2, tmp3;
+
+ asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b"
+ : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf));
+
+}
+
+volatile uint64_t irq_received;
+
+static void cnt_overflow(isr_regs_t *regs)
+{
+ irq_received++;
+ apic_write(APIC_EOI, 0);
+}
+
+static bool check_irq(void)
+{
+ int i;
+ irq_received = 0;
+ irq_enable();
+ for (i = 0; i < 100000 && !irq_received; i++)
+ asm volatile("pause");
+ irq_disable();
+ return irq_received;
+}
+
+static bool is_gp(pmu_counter_t *evt)
+{
+ return evt->ctr < MSR_CORE_PERF_FIXED_CTR0;
+}
+
+static int event_to_global_idx(pmu_counter_t *cnt)
+{
+ return cnt->ctr - (is_gp(cnt) ? MSR_IA32_PERFCTR0 :
+ (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
+}
+
+static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
+{
+ if (is_gp(cnt)) {
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+ if (gp_events[i].unit_sel == (cnt->config & 0xffff))
+ return &gp_events[i];
+ } else
+ return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0];
+
+ return (void*)0;
+}
+
+static void global_enable(pmu_counter_t *cnt)
+{
+ cnt->idx = event_to_global_idx(cnt);
+
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) |
+ (1ull << cnt->idx));
+}
+
+static void global_disable(pmu_counter_t *cnt)
+{
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) &
+ ~(1ull << cnt->idx));
+}
+
+
+static void start_event(pmu_counter_t *evt)
+{
+ wrmsr(evt->ctr, evt->count);
+ if (is_gp(evt))
+ wrmsr(MSR_P6_EVNTSEL0 + 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;
+ uint32_t usrospmi = 0;
+
+ if (evt->config & EVNTSEL_OS)
+ usrospmi |= (1 << 0);
+ if (evt->config & EVNTSEL_USR)
+ usrospmi |= (1 << 1);
+ if (evt->config & EVNTSEL_INT)
+ usrospmi |= (1 << 3); // PMI on overflow
+ ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift);
+ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl);
+ }
+ global_enable(evt);
+}
+
+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);
+ else {
+ uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+ int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
+ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift));
+ }
+ evt->count = rdmsr(evt->ctr);
+}
+
+static void measure(pmu_counter_t *evt, int count)
+{
+ int i;
+ for (i = 0; i < count; i++)
+ start_event(&evt[i]);
+ loop();
+ for (i = 0; i < count; i++)
+ stop_event(&evt[i]);
+}
+
+static bool verify_event(uint64_t count, struct pmu_event *e)
+{
+ // printf("%lld >= %lld <= %lld\n", e->min, count, e->max);
+ return count >= e->min && count <= e->max;
+
+}
+
+static bool verify_counter(pmu_counter_t *cnt)
+{
+ return verify_event(cnt->count, get_counter_event(cnt));
+}
+
+static void check_gp_counter(struct pmu_event *evt)
+{
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
+ };
+ int i;
+
+ for (i = 0; i < num_counters; i++, cnt.ctr++) {
+ cnt.count = 0;
+ measure(&cnt, 1);
+ report("%s-%d", verify_event(cnt.count, evt), evt->name, i);
+ }
+}
+
+static void check_gp_counters(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+ if (!(ebx.full & (1 << i)))
+ check_gp_counter(&gp_events[i]);
+ else
+ printf("GP event '%s' is disabled\n",
+ gp_events[i].name);
+}
+
+static void check_fixed_counters(void)
+{
+ pmu_counter_t cnt = {
+ .config = EVNTSEL_OS | EVNTSEL_USR,
+ };
+ int i;
+
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ cnt.count = 0;
+ cnt.ctr = fixed_events[i].unit_sel;
+ measure(&cnt, 1);
+ report("fixed-%d", verify_event(cnt.count, &fixed_events[i]), i);
+ }
+}
+
+static void check_counters_many(void)
+{
+ pmu_counter_t cnt[10];
+ int i, n;
+
+ for (i = 0, n = 0; n < num_counters; i++) {
+ if (ebx.full & (1 << i))
+ continue;
+
+ cnt[n].count = 0;
+ cnt[n].ctr = MSR_IA32_PERFCTR0 + n;
+ cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | gp_events[i].unit_sel;
+ n++;
+ }
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ cnt[n].count = 0;
+ cnt[n].ctr = fixed_events[i].unit_sel;
+ cnt[n].config = EVNTSEL_OS | EVNTSEL_USR;
+ n++;
+ }
+
+ measure(cnt, n);
+
+ for (i = 0; i < n; i++)
+ if (!verify_counter(&cnt[i]))
+ break;
+
+ report("all counters", i == n);
+}
+
+static void check_counter_overflow(void)
+{
+ uint64_t count;
+ int i;
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
+ .count = 0,
+ };
+ measure(&cnt, 1);
+ count = cnt.count;
+
+ /* clear status before test */
+ wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_STATUS));
+
+ report_prefix_push("overflow");
+
+ for (i = 0; i < num_counters + 1; i++, cnt.ctr++) {
+ uint64_t status;
+ int idx;
+ if (i == num_counters)
+ cnt.ctr = fixed_events[0].unit_sel;
+ if (i % 2)
+ cnt.config |= EVNTSEL_INT;
+ else
+ cnt.config &= ~EVNTSEL_INT;
+ idx = event_to_global_idx(&cnt);
+ cnt.count = 1 - count;
+ measure(&cnt, 1);
+ report("cntr-%d", cnt.count == 1, i);
+ status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+ report("status-%d", status & (1ull << idx), i);
+ wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
+ status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+ report("status clear-%d", !(status & (1ull << idx)), i);
+ report("irq-%d", check_irq() == (i % 2), i);
+ }
+
+ report_prefix_pop();
+}
+
+static void check_gp_counter_cmask(void)
+{
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
+ .count = 0,
+ };
+ cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
+ measure(&cnt, 1);
+ report("cmask", cnt.count < gp_events[1].min);
+}
+
+static void check_rdpmc(void)
+{
+ uint64_t val = 0x1f3456789ull;
+ int i;
+
+ report_prefix_push("rdpmc");
+
+ for (i = 0; i < num_counters; i++) {
+ uint64_t x = (val & 0xffffffff) |
+ ((1ull << (eax.split.bit_width - 32)) - 1) << 32;
+ wrmsr(MSR_IA32_PERFCTR0 + i, val);
+ report("cntr-%d", rdpmc(i) == x, i);
+ report("fast-%d", rdpmc(i | (1<<31)) == (u32)val, i);
+ }
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ uint64_t x = (val & 0xffffffff) |
+ ((1ull << (edx.split.bit_width_fixed - 32)) - 1) << 32;
+ wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, val);
+ report("fixed cntr-%d", rdpmc(i | (1 << 30)) == x, i);
+ report("fixed fast-%d", rdpmc(i | (3<<30)) == (u32)val, i);
+ }
+
+ report_prefix_pop();
+}
+
+int main(int ac, char **av)
+{
+ struct cpuid id = cpuid(10);
+
+ setup_vm();
+ setup_idt();
+ handle_irq(PC_VECTOR, cnt_overflow);
+ buf = vmalloc(N*64);
+
+ eax.full = id.a;
+ ebx.full = id.b;
+ edx.full = id.d;
+
+ if (!eax.split.version_id) {
+ printf("No pmu is detected!\n");
+ return report_summary();
+ }
+ printf("PMU version: %d\n", eax.split.version_id);
+ printf("GP counters: %d\n", eax.split.num_counters);
+ printf("GP counter width: %d\n", eax.split.bit_width);
+ printf("Mask length: %d\n", eax.split.mask_length);
+ printf("Fixed counters: %d\n", edx.split.num_counters_fixed);
+ printf("Fixed counter width: %d\n", edx.split.bit_width_fixed);
+
+ num_counters = eax.split.num_counters;
+ if (num_counters > ARRAY_SIZE(gp_events))
+ num_counters = ARRAY_SIZE(gp_events);
+
+ apic_write(APIC_LVTPC, PC_VECTOR);
+
+ check_gp_counters();
+ check_fixed_counters();
+ check_rdpmc();
+ check_counters_many();
+ check_counter_overflow();
+ check_gp_counter_cmask();
+
+ return report_summary();
+}
diff --git a/x86/pmu.c b/x86/pmu.c
deleted file mode 100644
index 1b818c2..0000000
--- a/x86/pmu.c
+++ /dev/null
@@ -1,379 +0,0 @@
-
-#include "x86/msr.h"
-#include "x86/processor.h"
-#include "x86/apic-defs.h"
-#include "x86/apic.h"
-#include "x86/desc.h"
-#include "x86/isr.h"
-#include "x86/vm.h"
-
-#include "libcflat.h"
-#include "pmu.h"
-#include <stdint.h>
-
-union cpuid10_eax {
- struct {
- unsigned int version_id:8;
- unsigned int num_counters:8;
- unsigned int bit_width:8;
- unsigned int mask_length:8;
- } split;
- unsigned int full;
-} eax;
-
-union cpuid10_ebx {
- struct {
- unsigned int no_unhalted_core_cycles:1;
- unsigned int no_instructions_retired:1;
- unsigned int no_unhalted_reference_cycles:1;
- unsigned int no_llc_reference:1;
- unsigned int no_llc_misses:1;
- unsigned int no_branch_instruction_retired:1;
- unsigned int no_branch_misses_retired:1;
- } split;
- unsigned int full;
-} ebx;
-
-union cpuid10_edx {
- struct {
- unsigned int num_counters_fixed:5;
- unsigned int bit_width_fixed:8;
- unsigned int reserved:19;
- } split;
- unsigned int full;
-} edx;
-
-struct pmu_event gp_events[] = {
- {"core cycles", 0x003c, 1*N, 50*N},
- {"instructions", 0x00c0, 10*N, 10.2*N},
- {"ref cycles", 0x013c, 0.1*N, 30*N},
- {"llc refference", 0x4f2e, 1, 2*N},
- {"llc misses", 0x412e, 1, 1*N},
- {"branches", 0x00c4, 1*N, 1.1*N},
- {"branch misses", 0x00c5, 0, 0.1*N},
-}, fixed_events[] = {
- {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N},
- {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N},
- {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
-};
-
-static int num_counters;
-
-char *buf;
-
-static inline void loop()
-{
- unsigned long tmp, tmp2, tmp3;
-
- asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b"
- : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf));
-
-}
-
-volatile uint64_t irq_received;
-
-static void cnt_overflow(isr_regs_t *regs)
-{
- irq_received++;
- apic_write(APIC_EOI, 0);
-}
-
-static bool check_irq(void)
-{
- int i;
- irq_received = 0;
- irq_enable();
- for (i = 0; i < 100000 && !irq_received; i++)
- asm volatile("pause");
- irq_disable();
- return irq_received;
-}
-
-static bool is_gp(pmu_counter_t *evt)
-{
- return evt->ctr < MSR_CORE_PERF_FIXED_CTR0;
-}
-
-static int event_to_global_idx(pmu_counter_t *cnt)
-{
- return cnt->ctr - (is_gp(cnt) ? MSR_IA32_PERFCTR0 :
- (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
-}
-
-static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
-{
- if (is_gp(cnt)) {
- int i;
-
- for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
- if (gp_events[i].unit_sel == (cnt->config & 0xffff))
- return &gp_events[i];
- } else
- return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0];
-
- return (void*)0;
-}
-
-static void global_enable(pmu_counter_t *cnt)
-{
- cnt->idx = event_to_global_idx(cnt);
-
- wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) |
- (1ull << cnt->idx));
-}
-
-static void global_disable(pmu_counter_t *cnt)
-{
- wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) &
- ~(1ull << cnt->idx));
-}
-
-
-static void start_event(pmu_counter_t *evt)
-{
- wrmsr(evt->ctr, evt->count);
- if (is_gp(evt))
- wrmsr(MSR_P6_EVNTSEL0 + 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;
- uint32_t usrospmi = 0;
-
- if (evt->config & EVNTSEL_OS)
- usrospmi |= (1 << 0);
- if (evt->config & EVNTSEL_USR)
- usrospmi |= (1 << 1);
- if (evt->config & EVNTSEL_INT)
- usrospmi |= (1 << 3); // PMI on overflow
- ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift);
- wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl);
- }
- global_enable(evt);
-}
-
-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);
- else {
- uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
- int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
- wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift));
- }
- evt->count = rdmsr(evt->ctr);
-}
-
-static void measure(pmu_counter_t *evt, int count)
-{
- int i;
- for (i = 0; i < count; i++)
- start_event(&evt[i]);
- loop();
- for (i = 0; i < count; i++)
- stop_event(&evt[i]);
-}
-
-static bool verify_event(uint64_t count, struct pmu_event *e)
-{
- // printf("%lld >= %lld <= %lld\n", e->min, count, e->max);
- return count >= e->min && count <= e->max;
-
-}
-
-static bool verify_counter(pmu_counter_t *cnt)
-{
- return verify_event(cnt->count, get_counter_event(cnt));
-}
-
-static void check_gp_counter(struct pmu_event *evt)
-{
- pmu_counter_t cnt = {
- .ctr = MSR_IA32_PERFCTR0,
- .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
- };
- int i;
-
- for (i = 0; i < num_counters; i++, cnt.ctr++) {
- cnt.count = 0;
- measure(&cnt, 1);
- report("%s-%d", verify_event(cnt.count, evt), evt->name, i);
- }
-}
-
-static void check_gp_counters(void)
-{
- int i;
-
- for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
- if (!(ebx.full & (1 << i)))
- check_gp_counter(&gp_events[i]);
- else
- printf("GP event '%s' is disabled\n",
- gp_events[i].name);
-}
-
-static void check_fixed_counters(void)
-{
- pmu_counter_t cnt = {
- .config = EVNTSEL_OS | EVNTSEL_USR,
- };
- int i;
-
- for (i = 0; i < edx.split.num_counters_fixed; i++) {
- cnt.count = 0;
- cnt.ctr = fixed_events[i].unit_sel;
- measure(&cnt, 1);
- report("fixed-%d", verify_event(cnt.count, &fixed_events[i]), i);
- }
-}
-
-static void check_counters_many(void)
-{
- pmu_counter_t cnt[10];
- int i, n;
-
- for (i = 0, n = 0; n < num_counters; i++) {
- if (ebx.full & (1 << i))
- continue;
-
- cnt[n].count = 0;
- cnt[n].ctr = MSR_IA32_PERFCTR0 + n;
- cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | gp_events[i].unit_sel;
- n++;
- }
- for (i = 0; i < edx.split.num_counters_fixed; i++) {
- cnt[n].count = 0;
- cnt[n].ctr = fixed_events[i].unit_sel;
- cnt[n].config = EVNTSEL_OS | EVNTSEL_USR;
- n++;
- }
-
- measure(cnt, n);
-
- for (i = 0; i < n; i++)
- if (!verify_counter(&cnt[i]))
- break;
-
- report("all counters", i == n);
-}
-
-static void check_counter_overflow(void)
-{
- uint64_t count;
- int i;
- pmu_counter_t cnt = {
- .ctr = MSR_IA32_PERFCTR0,
- .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
- .count = 0,
- };
- measure(&cnt, 1);
- count = cnt.count;
-
- /* clear status before test */
- wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_STATUS));
-
- report_prefix_push("overflow");
-
- for (i = 0; i < num_counters + 1; i++, cnt.ctr++) {
- uint64_t status;
- int idx;
- if (i == num_counters)
- cnt.ctr = fixed_events[0].unit_sel;
- if (i % 2)
- cnt.config |= EVNTSEL_INT;
- else
- cnt.config &= ~EVNTSEL_INT;
- idx = event_to_global_idx(&cnt);
- cnt.count = 1 - count;
- measure(&cnt, 1);
- report("cntr-%d", cnt.count == 1, i);
- status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
- report("status-%d", status & (1ull << idx), i);
- wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
- status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
- report("status clear-%d", !(status & (1ull << idx)), i);
- report("irq-%d", check_irq() == (i % 2), i);
- }
-
- report_prefix_pop();
-}
-
-static void check_gp_counter_cmask(void)
-{
- pmu_counter_t cnt = {
- .ctr = MSR_IA32_PERFCTR0,
- .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
- .count = 0,
- };
- cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
- measure(&cnt, 1);
- report("cmask", cnt.count < gp_events[1].min);
-}
-
-static void check_rdpmc(void)
-{
- uint64_t val = 0x1f3456789ull;
- int i;
-
- report_prefix_push("rdpmc");
-
- for (i = 0; i < num_counters; i++) {
- uint64_t x = (val & 0xffffffff) |
- ((1ull << (eax.split.bit_width - 32)) - 1) << 32;
- wrmsr(MSR_IA32_PERFCTR0 + i, val);
- report("cntr-%d", rdpmc(i) == x, i);
- report("fast-%d", rdpmc(i | (1<<31)) == (u32)val, i);
- }
- for (i = 0; i < edx.split.num_counters_fixed; i++) {
- uint64_t x = (val & 0xffffffff) |
- ((1ull << (edx.split.bit_width_fixed - 32)) - 1) << 32;
- wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, val);
- report("fixed cntr-%d", rdpmc(i | (1 << 30)) == x, i);
- report("fixed fast-%d", rdpmc(i | (3<<30)) == (u32)val, i);
- }
-
- report_prefix_pop();
-}
-
-int main(int ac, char **av)
-{
- struct cpuid id = cpuid(10);
-
- setup_vm();
- setup_idt();
- handle_irq(PC_VECTOR, cnt_overflow);
- buf = vmalloc(N*64);
-
- eax.full = id.a;
- ebx.full = id.b;
- edx.full = id.d;
-
- if (!eax.split.version_id) {
- printf("No pmu is detected!\n");
- return report_summary();
- }
- printf("PMU version: %d\n", eax.split.version_id);
- printf("GP counters: %d\n", eax.split.num_counters);
- printf("GP counter width: %d\n", eax.split.bit_width);
- printf("Mask length: %d\n", eax.split.mask_length);
- printf("Fixed counters: %d\n", edx.split.num_counters_fixed);
- printf("Fixed counter width: %d\n", edx.split.bit_width_fixed);
-
- num_counters = eax.split.num_counters;
- if (num_counters > ARRAY_SIZE(gp_events))
- num_counters = ARRAY_SIZE(gp_events);
-
- apic_write(APIC_LVTPC, PC_VECTOR);
-
- check_gp_counters();
- check_fixed_counters();
- check_rdpmc();
- check_counters_many();
- check_counter_overflow();
- check_gp_counter_cmask();
-
- return report_summary();
-}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index b4e5172..74156fa 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -140,8 +140,8 @@ arch = x86_64
[msr]
file = msr.flat
-[pmu]
-file = pmu.flat
+[intel_pmu]
+file = intel_pmu.flat
extra_params = -cpu host
check = /proc/sys/kernel/nmi_watchdog=0
--
2.7.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code
2017-08-04 4:24 [kvm-unit-tests RFC 0/3] x86/pmu: Add AMD vPMU testing code Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c Wei Huang
@ 2017-08-04 4:24 ` Wei Huang
2017-08-04 8:14 ` Andrew Jones
2 siblings, 1 reply; 9+ messages in thread
From: Wei Huang @ 2017-08-04 4:24 UTC (permalink / raw)
To: kvm; +Cc: pbonzini, rkrcmar, wei
AMD PMU implementation is very different from Intel. This patch adds
a new test case, called amd_pmu, to x86 architecture. The
implementation of amd_pmu.c is based on intel_pmu.c, with focus on
AMD's general-purpose registers. To avoid running on Intel CPUs,
we check the CPU's vendor name before executing the tests.
Signed-off-by: Wei Huang <wei@redhat.com>
---
x86/Makefile.common | 3 +-
x86/amd_pmu.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++
x86/unittests.cfg | 4 +
3 files changed, 271 insertions(+), 1 deletion(-)
create mode 100644 x86/amd_pmu.c
diff --git a/x86/Makefile.common b/x86/Makefile.common
index d0f4ed1..36bcf90 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -45,7 +45,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
$(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
$(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
$(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \
- $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat $(TEST_DIR)/setjmp.flat \
+ $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat \
+ $(TEST_DIR)/amd_pmu.flat $(TEST_DIR)/setjmp.flat \
$(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
$(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
$(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
diff --git a/x86/amd_pmu.c b/x86/amd_pmu.c
new file mode 100644
index 0000000..006eccf
--- /dev/null
+++ b/x86/amd_pmu.c
@@ -0,0 +1,265 @@
+/*
+ * vPMU testing for AMD CPUs
+ *
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include "pmu.h"
+#include <stdint.h>
+
+#define AMD64_NUM_COUNTERS 4
+
+#define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */
+#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */
+#define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */
+
+struct pmu_event gp_events[] = {
+ {"core cycles", 0x0076, 1*N, 50*N},
+ {"instructions", 0x00c0, 10*N, 10.2*N},
+ {"cache reference", 0x077d, 1, 2*N},
+ {"cache misses", 0x077e, 0, 1*N},
+ {"branches", 0x00c2, 1*N, 1.1*N},
+ {"branch misses", 0x00c3, 0, 0.1*N},
+ {"stalls frontend", 0x00d0, 0, 0.1*N},
+ {"stalls backend", 0x00d1, 0, 30*N},
+};
+
+char *buf;
+static int num_counters;
+volatile uint64_t irq_received;
+
+static bool check_irq(void)
+{
+ int i;
+ irq_received = 0;
+
+ irq_enable();
+ for (i = 0; i < 100000 && !irq_received; i++)
+ asm volatile("pause");
+ irq_disable();
+
+ return irq_received;
+}
+
+static inline void loop()
+{
+ unsigned long tmp, tmp2, tmp3;
+
+ asm volatile("1: mov (%1), %2\n\t"
+ " add $64, %1\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " nop\n\t"
+ " loop 1b"
+ : "=c"(tmp), "=r"(tmp2), "=r"(tmp3)
+ : "0"(N), "1"(buf));
+}
+
+static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+ if (gp_events[i].unit_sel == (cnt->config & 0xffff))
+ return &gp_events[i];
+
+ return (void*)0;
+}
+
+static void cnt_overflow(isr_regs_t *regs)
+{
+ irq_received++;
+ apic_write(APIC_EOI, 0);
+}
+
+static int event_to_global_idx(pmu_counter_t *cnt)
+{
+ return cnt->ctr - MSR_K7_PERFCTR0;
+}
+
+static bool verify_event(uint64_t count, struct pmu_event *e)
+{
+ return count >= e->min && count <= e->max;
+}
+
+static bool verify_counter(pmu_counter_t *cnt)
+{
+ return verify_event(cnt->count, get_counter_event(cnt));
+}
+
+static void start_event(pmu_counter_t *evt)
+{
+ wrmsr(evt->ctr, evt->count);
+ wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
+ evt->config | EVNTSEL_EN);
+}
+
+static void stop_event(pmu_counter_t *evt)
+{
+ wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
+ evt->config & ~EVNTSEL_EN);
+ evt->count = rdmsr(evt->ctr);
+}
+
+static void measure(pmu_counter_t *evt, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ start_event(&evt[i]);
+
+ loop();
+
+ for (i = 0; i < count; i++)
+ stop_event(&evt[i]);
+}
+
+static void check_gp_counter(struct pmu_event *evt)
+{
+ int i;
+
+ pmu_counter_t cnt = {
+ .ctr = MSR_K7_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
+ };
+
+ for (i = 0; i < num_counters; i++, cnt.ctr++) {
+ cnt.count = 0;
+ measure(&cnt, 1);
+ report("%s-%d", verify_event(cnt.count, evt), evt->name, i);
+ }
+}
+
+static void check_gp_counters(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) {
+ check_gp_counter(&gp_events[i]);
+ }
+}
+
+static void check_counter_overflow(void)
+{
+ uint64_t count;
+ int i;
+ pmu_counter_t cnt = {
+ .ctr = MSR_K7_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
+ .count = 0,
+ };
+
+ measure(&cnt, 1);
+ count = cnt.count;
+
+ report_prefix_push("overflow");
+
+ for (i = 0; i < num_counters; i++, cnt.ctr++) {
+ if (i % 2)
+ cnt.config |= EVNTSEL_INT;
+ else
+ cnt.config &= ~EVNTSEL_INT;
+ cnt.count = 1 - count;
+ measure(&cnt, 1);
+ report("cntr-%d", cnt.count < 20, i);
+ report("irq-%d", check_irq() == (i % 2), i);
+ }
+
+ report_prefix_pop();
+}
+
+static void check_gp_counter_cmask(void)
+{
+ pmu_counter_t cnt = {
+ .ctr = MSR_K7_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
+ .count = 0,
+ };
+
+ cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
+ measure(&cnt, 1);
+ report("cmask", cnt.count < gp_events[1].min);
+}
+
+static void check_rdpmc(void)
+{
+ uint64_t val = 0x1f3456789ull;
+ int i;
+
+ report_prefix_push("rdpmc");
+
+ for (i = 0; i < num_counters; i++) {
+ uint64_t x = val;
+ wrmsr(MSR_K7_PERFCTR0 + i, val);
+ report("cntr-%d", rdpmc(i) == x, i);
+ }
+
+ report_prefix_pop();
+}
+
+static void check_counters_many(void)
+{
+ pmu_counter_t cnt[10];
+ int i, n;
+
+ for (n = 0; n < num_counters; n++) {
+ cnt[n].count = 0;
+ cnt[n].ctr = MSR_K7_PERFCTR0 + n;
+ cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | gp_events[n].unit_sel;
+ }
+
+ measure(cnt, n);
+
+ for (i = 0; i < n; i++)
+ if (!verify_counter(&cnt[i]))
+ break;
+
+ report("all counters", i == n);
+}
+
+#define IS_AMD_CPU(cpuid) ((cpuid).b == CPUID_VENDOR_AMD_1 && \
+ (cpuid).d == CPUID_VENDOR_AMD_2 && \
+ (cpuid).c == CPUID_VENDOR_AMD_3)
+int main(int ac, char **av)
+{
+ struct cpuid id = cpuid(0);
+
+ setup_vm();
+ setup_idt();
+ handle_irq(PC_VECTOR, cnt_overflow);
+ buf = vmalloc(N * 64);
+
+ if (!IS_AMD_CPU(id)) {
+ printf("No AMD PMU detected!\n");
+ return report_summary();
+ }
+
+ num_counters = AMD64_NUM_COUNTERS;
+ if (num_counters > ARRAY_SIZE(gp_events))
+ num_counters = ARRAY_SIZE(gp_events);
+
+ apic_write(APIC_LVTPC, PC_VECTOR);
+
+ check_gp_counters();
+ check_rdpmc();
+ check_counters_many();
+ check_counter_overflow();
+ check_gp_counter_cmask();
+
+ return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 74156fa..98fb3e9 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -145,6 +145,10 @@ file = intel_pmu.flat
extra_params = -cpu host
check = /proc/sys/kernel/nmi_watchdog=0
+[amd_pmu]
+file = amd_pmu.flat
+extra_params = -cpu host
+
[port80]
file = port80.flat
--
2.7.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h
2017-08-04 4:24 ` [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h Wei Huang
@ 2017-08-04 7:59 ` Andrew Jones
0 siblings, 0 replies; 9+ messages in thread
From: Andrew Jones @ 2017-08-04 7:59 UTC (permalink / raw)
To: Wei Huang; +Cc: kvm, pbonzini, rkrcmar
On Thu, Aug 03, 2017 at 11:24:28PM -0500, Wei Huang wrote:
> This patch moves common x86 PMU counter and event defintions to pmu.h
> file. This header file will be used by both Intel and AMD PMU test
> code.
>
> Signed-off-by: Wei Huang <wei@redhat.com>
> ---
> x86/pmu.c | 38 ++------------------------------------
> x86/pmu.h | 42 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 44 insertions(+), 36 deletions(-)
> create mode 100644 x86/pmu.h
>
> diff --git a/x86/pmu.c b/x86/pmu.c
> index a0238dc..1b818c2 100644
> --- a/x86/pmu.c
> +++ b/x86/pmu.c
> @@ -8,38 +8,9 @@
> #include "x86/vm.h"
>
> #include "libcflat.h"
> +#include "pmu.h"
> #include <stdint.h>
>
> -#define FIXED_CNT_INDEX 32
> -#define PC_VECTOR 32
> -
> -#define EVNSEL_EVENT_SHIFT 0
> -#define EVNTSEL_UMASK_SHIFT 8
> -#define EVNTSEL_USR_SHIFT 16
> -#define EVNTSEL_OS_SHIFT 17
> -#define EVNTSEL_EDGE_SHIFT 18
> -#define EVNTSEL_PC_SHIFT 19
> -#define EVNTSEL_INT_SHIFT 20
> -#define EVNTSEL_EN_SHIF 22
> -#define EVNTSEL_INV_SHIF 23
> -#define EVNTSEL_CMASK_SHIFT 24
> -
> -#define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF)
> -#define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT)
> -#define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT)
> -#define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT)
> -#define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT)
> -#define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF)
> -
> -#define N 1000000
> -
> -typedef struct {
> - uint32_t ctr;
> - uint32_t config;
> - uint64_t count;
> - int idx;
> -} pmu_counter_t;
> -
> union cpuid10_eax {
> struct {
> unsigned int version_id:8;
> @@ -72,12 +43,7 @@ union cpuid10_edx {
> unsigned int full;
> } edx;
>
> -struct pmu_event {
> - const char *name;
> - uint32_t unit_sel;
> - int min;
> - int max;
> -} gp_events[] = {
> +struct pmu_event gp_events[] = {
> {"core cycles", 0x003c, 1*N, 50*N},
> {"instructions", 0x00c0, 10*N, 10.2*N},
> {"ref cycles", 0x013c, 0.1*N, 30*N},
> diff --git a/x86/pmu.h b/x86/pmu.h
> new file mode 100644
> index 0000000..b1e8e51
> --- /dev/null
> +++ b/x86/pmu.h
> @@ -0,0 +1,42 @@
> +/* vPMU unit testing for x86 */
> +#ifndef __PMU_H
> +#define __PMU_H
New files should get GPL headers.
> +
> +#define N 1000000
> +
> +#define FIXED_CNT_INDEX 32
> +#define PC_VECTOR 32
> +
> +#define EVNTSEL_EVENT_SHIFT 0
> +#define EVNTSEL_UMASK_SHIFT 8
> +#define EVNTSEL_USR_SHIFT 16
> +#define EVNTSEL_OS_SHIFT 17
> +#define EVNTSEL_EDGE_SHIFT 18
> +#define EVNTSEL_PC_SHIFT 19
> +#define EVNTSEL_INT_SHIFT 20
> +#define EVNTSEL_EN_SHIF 22
> +#define EVNTSEL_INV_SHIF 23
> +#define EVNTSEL_CMASK_SHIFT 24
> +
> +#define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF)
> +#define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT)
> +#define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT)
> +#define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT)
> +#define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT)
> +#define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF)
> +
> +typedef struct {
> + uint32_t ctr;
> + uint32_t config;
> + uint64_t count;
> + int idx;
> +} pmu_counter_t;
> +
> +struct pmu_event {
> + const char *name;
> + uint32_t unit_sel;
> + int min;
> + int max;
> +};
> +
> +#endif /* __PMU_H */
> --
> 2.7.5
>
Otherwise
Reviewed-by: Andrew Jones <drjones@redhat.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c
2017-08-04 4:24 ` [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c Wei Huang
@ 2017-08-04 8:00 ` Andrew Jones
2017-08-04 15:44 ` Wei Huang
0 siblings, 1 reply; 9+ messages in thread
From: Andrew Jones @ 2017-08-04 8:00 UTC (permalink / raw)
To: Wei Huang; +Cc: kvm, pbonzini, rkrcmar
On Thu, Aug 03, 2017 at 11:24:29PM -0500, Wei Huang wrote:
> Many code in pmu.c is Intel-specific (e.g. fixed counters, global
> ctrl). This patch renames pmu.c to intel_pmu.c. The makefile and
> unit-test config file are also changed acccordingly.
>
> Signed-off-by: Wei Huang <wei@redhat.com>
> ---
> x86/Makefile.common | 2 +-
> x86/intel_pmu.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> x86/pmu.c | 379 ----------------------------------------------------
> x86/unittests.cfg | 4 +-
> 4 files changed, 382 insertions(+), 382 deletions(-)
> create mode 100644 x86/intel_pmu.c
> delete mode 100644 x86/pmu.c
It'd be best to create this patch with 'format-patch -M'
Thanks,
drew
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code
2017-08-04 4:24 ` [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code Wei Huang
@ 2017-08-04 8:14 ` Andrew Jones
2017-08-04 15:49 ` Wei Huang
0 siblings, 1 reply; 9+ messages in thread
From: Andrew Jones @ 2017-08-04 8:14 UTC (permalink / raw)
To: Wei Huang; +Cc: kvm, pbonzini, rkrcmar
Hi Wei,
I didn't review the important part of this test, i.e. what it's
testing and how, so below are just some nits.
On Thu, Aug 03, 2017 at 11:24:30PM -0500, Wei Huang wrote:
> AMD PMU implementation is very different from Intel. This patch adds
> a new test case, called amd_pmu, to x86 architecture. The
> implementation of amd_pmu.c is based on intel_pmu.c, with focus on
> AMD's general-purpose registers. To avoid running on Intel CPUs,
> we check the CPU's vendor name before executing the tests.
>
> Signed-off-by: Wei Huang <wei@redhat.com>
> ---
> x86/Makefile.common | 3 +-
> x86/amd_pmu.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> x86/unittests.cfg | 4 +
> 3 files changed, 271 insertions(+), 1 deletion(-)
> create mode 100644 x86/amd_pmu.c
>
> diff --git a/x86/Makefile.common b/x86/Makefile.common
> index d0f4ed1..36bcf90 100644
> --- a/x86/Makefile.common
> +++ b/x86/Makefile.common
> @@ -45,7 +45,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
> $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
> $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
> $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \
> - $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat $(TEST_DIR)/setjmp.flat \
> + $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat \
> + $(TEST_DIR)/amd_pmu.flat $(TEST_DIR)/setjmp.flat \
mixing tabs and spaces here
> $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
> $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
> $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
> diff --git a/x86/amd_pmu.c b/x86/amd_pmu.c
> new file mode 100644
> index 0000000..006eccf
> --- /dev/null
> +++ b/x86/amd_pmu.c
> @@ -0,0 +1,265 @@
> +/*
> + * vPMU testing for AMD CPUs
> + *
> + * Copyright (c) 2017 Red Hat Inc
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.
> + */
> +
> +#include "x86/msr.h"
> +#include "x86/processor.h"
> +#include "x86/apic-defs.h"
> +#include "x86/apic.h"
apic-defs is included by apic
> +#include "x86/desc.h"
> +#include "x86/isr.h"
> +#include "x86/vm.h"
> +
> +#include "libcflat.h"
> +#include "pmu.h"
> +#include <stdint.h>
stdint is included by libcflat
> +
> +#define AMD64_NUM_COUNTERS 4
> +
> +#define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */
> +#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */
> +#define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */
> +
> +struct pmu_event gp_events[] = {
> + {"core cycles", 0x0076, 1*N, 50*N},
> + {"instructions", 0x00c0, 10*N, 10.2*N},
> + {"cache reference", 0x077d, 1, 2*N},
> + {"cache misses", 0x077e, 0, 1*N},
> + {"branches", 0x00c2, 1*N, 1.1*N},
> + {"branch misses", 0x00c3, 0, 0.1*N},
> + {"stalls frontend", 0x00d0, 0, 0.1*N},
> + {"stalls backend", 0x00d1, 0, 30*N},
> +};
> +
> +char *buf;
> +static int num_counters;
> +volatile uint64_t irq_received;
all above globals can be static
> +
> +static bool check_irq(void)
> +{
> + int i;
> + irq_received = 0;
> +
> + irq_enable();
> + for (i = 0; i < 100000 && !irq_received; i++)
> + asm volatile("pause");
> + irq_disable();
> +
> + return irq_received;
> +}
> +
> +static inline void loop()
> +{
> + unsigned long tmp, tmp2, tmp3;
> +
> + asm volatile("1: mov (%1), %2\n\t"
> + " add $64, %1\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " nop\n\t"
> + " loop 1b"
> + : "=c"(tmp), "=r"(tmp2), "=r"(tmp3)
> + : "0"(N), "1"(buf));
> +}
> +
> +static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
> +{
> + int i;
> +
> + for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
> + if (gp_events[i].unit_sel == (cnt->config & 0xffff))
> + return &gp_events[i];
> +
> + return (void*)0;
NULL
> +}
> +
> +static void cnt_overflow(isr_regs_t *regs)
> +{
> + irq_received++;
> + apic_write(APIC_EOI, 0);
> +}
> +
> +static int event_to_global_idx(pmu_counter_t *cnt)
> +{
> + return cnt->ctr - MSR_K7_PERFCTR0;
> +}
> +
> +static bool verify_event(uint64_t count, struct pmu_event *e)
> +{
> + return count >= e->min && count <= e->max;
> +}
> +
> +static bool verify_counter(pmu_counter_t *cnt)
> +{
> + return verify_event(cnt->count, get_counter_event(cnt));
> +}
> +
> +static void start_event(pmu_counter_t *evt)
> +{
> + wrmsr(evt->ctr, evt->count);
> + wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
> + evt->config | EVNTSEL_EN);
> +}
> +
> +static void stop_event(pmu_counter_t *evt)
> +{
> + wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
> + evt->config & ~EVNTSEL_EN);
> + evt->count = rdmsr(evt->ctr);
> +}
> +
> +static void measure(pmu_counter_t *evt, int count)
> +{
> + int i;
> +
> + for (i = 0; i < count; i++)
> + start_event(&evt[i]);
> +
> + loop();
> +
> + for (i = 0; i < count; i++)
> + stop_event(&evt[i]);
> +}
> +
> +static void check_gp_counter(struct pmu_event *evt)
> +{
> + int i;
> +
> + pmu_counter_t cnt = {
> + .ctr = MSR_K7_PERFCTR0,
> + .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
> + };
> +
> + for (i = 0; i < num_counters; i++, cnt.ctr++) {
> + cnt.count = 0;
> + measure(&cnt, 1);
> + report("%s-%d", verify_event(cnt.count, evt), evt->name, i);
> + }
> +}
> +
> +static void check_gp_counters(void)
> +{
> + int i;
> +
> + for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) {
> + check_gp_counter(&gp_events[i]);
> + }
> +}
> +
> +static void check_counter_overflow(void)
> +{
> + uint64_t count;
> + int i;
> + pmu_counter_t cnt = {
> + .ctr = MSR_K7_PERFCTR0,
> + .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
> + .count = 0,
> + };
> +
> + measure(&cnt, 1);
> + count = cnt.count;
> +
> + report_prefix_push("overflow");
> +
> + for (i = 0; i < num_counters; i++, cnt.ctr++) {
> + if (i % 2)
> + cnt.config |= EVNTSEL_INT;
> + else
> + cnt.config &= ~EVNTSEL_INT;
> + cnt.count = 1 - count;
> + measure(&cnt, 1);
> + report("cntr-%d", cnt.count < 20, i);
> + report("irq-%d", check_irq() == (i % 2), i);
> + }
> +
> + report_prefix_pop();
> +}
> +
> +static void check_gp_counter_cmask(void)
> +{
> + pmu_counter_t cnt = {
> + .ctr = MSR_K7_PERFCTR0,
> + .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
> + .count = 0,
> + };
> +
> + cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
> + measure(&cnt, 1);
> + report("cmask", cnt.count < gp_events[1].min);
> +}
> +
> +static void check_rdpmc(void)
> +{
> + uint64_t val = 0x1f3456789ull;
> + int i;
> +
> + report_prefix_push("rdpmc");
> +
> + for (i = 0; i < num_counters; i++) {
> + uint64_t x = val;
> + wrmsr(MSR_K7_PERFCTR0 + i, val);
> + report("cntr-%d", rdpmc(i) == x, i);
> + }
> +
> + report_prefix_pop();
> +}
> +
> +static void check_counters_many(void)
> +{
> + pmu_counter_t cnt[10];
> + int i, n;
> +
> + for (n = 0; n < num_counters; n++) {
> + cnt[n].count = 0;
> + cnt[n].ctr = MSR_K7_PERFCTR0 + n;
> + cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | gp_events[n].unit_sel;
> + }
> +
> + measure(cnt, n);
> +
> + for (i = 0; i < n; i++)
> + if (!verify_counter(&cnt[i]))
> + break;
{} for the for loop, might be nice here
> +
> + report("all counters", i == n);
> +}
> +
> +#define IS_AMD_CPU(cpuid) ((cpuid).b == CPUID_VENDOR_AMD_1 && \
> + (cpuid).d == CPUID_VENDOR_AMD_2 && \
> + (cpuid).c == CPUID_VENDOR_AMD_3)
should add a blank line here
> +int main(int ac, char **av)
> +{
> + struct cpuid id = cpuid(0);
> +
> + setup_vm();
> + setup_idt();
> + handle_irq(PC_VECTOR, cnt_overflow);
> + buf = vmalloc(N * 64);
> +
> + if (!IS_AMD_CPU(id)) {
> + printf("No AMD PMU detected!\n");
> + return report_summary();
> + }
> +
> + num_counters = AMD64_NUM_COUNTERS;
> + if (num_counters > ARRAY_SIZE(gp_events))
> + num_counters = ARRAY_SIZE(gp_events);
> +
> + apic_write(APIC_LVTPC, PC_VECTOR);
> +
> + check_gp_counters();
> + check_rdpmc();
> + check_counters_many();
> + check_counter_overflow();
> + check_gp_counter_cmask();
> +
> + return report_summary();
> +}
> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
> index 74156fa..98fb3e9 100644
> --- a/x86/unittests.cfg
> +++ b/x86/unittests.cfg
> @@ -145,6 +145,10 @@ file = intel_pmu.flat
> extra_params = -cpu host
> check = /proc/sys/kernel/nmi_watchdog=0
>
> +[amd_pmu]
> +file = amd_pmu.flat
> +extra_params = -cpu host
maybe add 'arch = x86_64', since AMD only supports 64 bit.
> +
> [port80]
> file = port80.flat
>
> --
> 2.7.5
>
Thanks,
drew
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c
2017-08-04 8:00 ` Andrew Jones
@ 2017-08-04 15:44 ` Wei Huang
0 siblings, 0 replies; 9+ messages in thread
From: Wei Huang @ 2017-08-04 15:44 UTC (permalink / raw)
To: Andrew Jones; +Cc: kvm, pbonzini, rkrcmar
On 08/04/2017 03:00 AM, Andrew Jones wrote:
> On Thu, Aug 03, 2017 at 11:24:29PM -0500, Wei Huang wrote:
>> Many code in pmu.c is Intel-specific (e.g. fixed counters, global
>> ctrl). This patch renames pmu.c to intel_pmu.c. The makefile and
>> unit-test config file are also changed acccordingly.
>>
>> Signed-off-by: Wei Huang <wei@redhat.com>
>> ---
>> x86/Makefile.common | 2 +-
>> x86/intel_pmu.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>> x86/pmu.c | 379 ----------------------------------------------------
>> x86/unittests.cfg | 4 +-
>> 4 files changed, 382 insertions(+), 382 deletions(-)
>> create mode 100644 x86/intel_pmu.c
>> delete mode 100644 x86/pmu.c
>
> It'd be best to create this patch with 'format-patch -M'
>
Thanks for pointing it out. I will do it in next re-spin.
> Thanks,
> drew
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code
2017-08-04 8:14 ` Andrew Jones
@ 2017-08-04 15:49 ` Wei Huang
0 siblings, 0 replies; 9+ messages in thread
From: Wei Huang @ 2017-08-04 15:49 UTC (permalink / raw)
To: Andrew Jones; +Cc: kvm, pbonzini, rkrcmar
On 08/04/2017 03:14 AM, Andrew Jones wrote:
> Hi Wei,
>
> I didn't review the important part of this test, i.e. what it's
> testing and how, so below are just some nits.
Thanks for reviewing it. I agree with most nits below, and will fix them
in the next respin.
>
> On Thu, Aug 03, 2017 at 11:24:30PM -0500, Wei Huang wrote:
>> AMD PMU implementation is very different from Intel. This patch adds
>> a new test case, called amd_pmu, to x86 architecture. The
>> implementation of amd_pmu.c is based on intel_pmu.c, with focus on
>> AMD's general-purpose registers. To avoid running on Intel CPUs,
>> we check the CPU's vendor name before executing the tests.
>>
>> Signed-off-by: Wei Huang <wei@redhat.com>
>> ---
>> x86/Makefile.common | 3 +-
>> x86/amd_pmu.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>> x86/unittests.cfg | 4 +
>> 3 files changed, 271 insertions(+), 1 deletion(-)
>> create mode 100644 x86/amd_pmu.c
>>
>> diff --git a/x86/Makefile.common b/x86/Makefile.common
>> index d0f4ed1..36bcf90 100644
>> --- a/x86/Makefile.common
>> +++ b/x86/Makefile.common
>> @@ -45,7 +45,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
>> $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
>> $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
>> $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \
>> - $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat $(TEST_DIR)/setjmp.flat \
>> + $(TEST_DIR)/s3.flat $(TEST_DIR)/intel_pmu.flat \
>> + $(TEST_DIR)/amd_pmu.flat $(TEST_DIR)/setjmp.flat \
>
> mixing tabs and spaces here
>
>> $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
>> $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
>> $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
>> diff --git a/x86/amd_pmu.c b/x86/amd_pmu.c
>> new file mode 100644
>> index 0000000..006eccf
>> --- /dev/null
>> +++ b/x86/amd_pmu.c
>> @@ -0,0 +1,265 @@
>> +/*
>> + * vPMU testing for AMD CPUs
>> + *
>> + * Copyright (c) 2017 Red Hat Inc
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2.
>> + */
>> +
>> +#include "x86/msr.h"
>> +#include "x86/processor.h"
>> +#include "x86/apic-defs.h"
>> +#include "x86/apic.h"
>
> apic-defs is included by apic
>
>> +#include "x86/desc.h"
>> +#include "x86/isr.h"
>> +#include "x86/vm.h"
>> +
>> +#include "libcflat.h"
>> +#include "pmu.h"
>> +#include <stdint.h>
>
> stdint is included by libcflat
>
>> +
>> +#define AMD64_NUM_COUNTERS 4
>> +
>> +#define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */
>> +#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */
>> +#define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */
>> +
>> +struct pmu_event gp_events[] = {
>> + {"core cycles", 0x0076, 1*N, 50*N},
>> + {"instructions", 0x00c0, 10*N, 10.2*N},
>> + {"cache reference", 0x077d, 1, 2*N},
>> + {"cache misses", 0x077e, 0, 1*N},
>> + {"branches", 0x00c2, 1*N, 1.1*N},
>> + {"branch misses", 0x00c3, 0, 0.1*N},
>> + {"stalls frontend", 0x00d0, 0, 0.1*N},
>> + {"stalls backend", 0x00d1, 0, 30*N},
>> +};
>> +
>> +char *buf;
>> +static int num_counters;
>> +volatile uint64_t irq_received;
>
> all above globals can be static
>
>> +
>> +static bool check_irq(void)
>> +{
>> + int i;
>> + irq_received = 0;
>> +
>> + irq_enable();
>> + for (i = 0; i < 100000 && !irq_received; i++)
>> + asm volatile("pause");
>> + irq_disable();
>> +
>> + return irq_received;
>> +}
>> +
>> +static inline void loop()
>> +{
>> + unsigned long tmp, tmp2, tmp3;
>> +
>> + asm volatile("1: mov (%1), %2\n\t"
>> + " add $64, %1\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " nop\n\t"
>> + " loop 1b"
>> + : "=c"(tmp), "=r"(tmp2), "=r"(tmp3)
>> + : "0"(N), "1"(buf));
>> +}
>> +
>> +static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
>> + if (gp_events[i].unit_sel == (cnt->config & 0xffff))
>> + return &gp_events[i];
>> +
>> + return (void*)0;
>
> NULL
>
>> +}
>> +
>> +static void cnt_overflow(isr_regs_t *regs)
>> +{
>> + irq_received++;
>> + apic_write(APIC_EOI, 0);
>> +}
>> +
>> +static int event_to_global_idx(pmu_counter_t *cnt)
>> +{
>> + return cnt->ctr - MSR_K7_PERFCTR0;
>> +}
>> +
>> +static bool verify_event(uint64_t count, struct pmu_event *e)
>> +{
>> + return count >= e->min && count <= e->max;
>> +}
>> +
>> +static bool verify_counter(pmu_counter_t *cnt)
>> +{
>> + return verify_event(cnt->count, get_counter_event(cnt));
>> +}
>> +
>> +static void start_event(pmu_counter_t *evt)
>> +{
>> + wrmsr(evt->ctr, evt->count);
>> + wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
>> + evt->config | EVNTSEL_EN);
>> +}
>> +
>> +static void stop_event(pmu_counter_t *evt)
>> +{
>> + wrmsr(MSR_K7_EVNTSEL0 + event_to_global_idx(evt),
>> + evt->config & ~EVNTSEL_EN);
>> + evt->count = rdmsr(evt->ctr);
>> +}
>> +
>> +static void measure(pmu_counter_t *evt, int count)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < count; i++)
>> + start_event(&evt[i]);
>> +
>> + loop();
>> +
>> + for (i = 0; i < count; i++)
>> + stop_event(&evt[i]);
>> +}
>> +
>> +static void check_gp_counter(struct pmu_event *evt)
>> +{
>> + int i;
>> +
>> + pmu_counter_t cnt = {
>> + .ctr = MSR_K7_PERFCTR0,
>> + .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
>> + };
>> +
>> + for (i = 0; i < num_counters; i++, cnt.ctr++) {
>> + cnt.count = 0;
>> + measure(&cnt, 1);
>> + report("%s-%d", verify_event(cnt.count, evt), evt->name, i);
>> + }
>> +}
>> +
>> +static void check_gp_counters(void)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) {
>> + check_gp_counter(&gp_events[i]);
>> + }
>> +}
>> +
>> +static void check_counter_overflow(void)
>> +{
>> + uint64_t count;
>> + int i;
>> + pmu_counter_t cnt = {
>> + .ctr = MSR_K7_PERFCTR0,
>> + .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
>> + .count = 0,
>> + };
>> +
>> + measure(&cnt, 1);
>> + count = cnt.count;
>> +
>> + report_prefix_push("overflow");
>> +
>> + for (i = 0; i < num_counters; i++, cnt.ctr++) {
>> + if (i % 2)
>> + cnt.config |= EVNTSEL_INT;
>> + else
>> + cnt.config &= ~EVNTSEL_INT;
>> + cnt.count = 1 - count;
>> + measure(&cnt, 1);
>> + report("cntr-%d", cnt.count < 20, i);
>> + report("irq-%d", check_irq() == (i % 2), i);
>> + }
>> +
>> + report_prefix_pop();
>> +}
>> +
>> +static void check_gp_counter_cmask(void)
>> +{
>> + pmu_counter_t cnt = {
>> + .ctr = MSR_K7_PERFCTR0,
>> + .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
>> + .count = 0,
>> + };
>> +
>> + cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
>> + measure(&cnt, 1);
>> + report("cmask", cnt.count < gp_events[1].min);
>> +}
>> +
>> +static void check_rdpmc(void)
>> +{
>> + uint64_t val = 0x1f3456789ull;
>> + int i;
>> +
>> + report_prefix_push("rdpmc");
>> +
>> + for (i = 0; i < num_counters; i++) {
>> + uint64_t x = val;
>> + wrmsr(MSR_K7_PERFCTR0 + i, val);
>> + report("cntr-%d", rdpmc(i) == x, i);
>> + }
>> +
>> + report_prefix_pop();
>> +}
>> +
>> +static void check_counters_many(void)
>> +{
>> + pmu_counter_t cnt[10];
>> + int i, n;
>> +
>> + for (n = 0; n < num_counters; n++) {
>> + cnt[n].count = 0;
>> + cnt[n].ctr = MSR_K7_PERFCTR0 + n;
>> + cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | gp_events[n].unit_sel;
>> + }
>> +
>> + measure(cnt, n);
>> +
>> + for (i = 0; i < n; i++)
>> + if (!verify_counter(&cnt[i]))
>> + break;
>
> {} for the for loop, might be nice here
>
>> +
>> + report("all counters", i == n);
>> +}
>> +
>> +#define IS_AMD_CPU(cpuid) ((cpuid).b == CPUID_VENDOR_AMD_1 && \
>> + (cpuid).d == CPUID_VENDOR_AMD_2 && \
>> + (cpuid).c == CPUID_VENDOR_AMD_3)
>
> should add a blank line here
>
>> +int main(int ac, char **av)
>> +{
>> + struct cpuid id = cpuid(0);
>> +
>> + setup_vm();
>> + setup_idt();
>> + handle_irq(PC_VECTOR, cnt_overflow);
>> + buf = vmalloc(N * 64);
>> +
>> + if (!IS_AMD_CPU(id)) {
>> + printf("No AMD PMU detected!\n");
>> + return report_summary();
>> + }
>> +
>> + num_counters = AMD64_NUM_COUNTERS;
>> + if (num_counters > ARRAY_SIZE(gp_events))
>> + num_counters = ARRAY_SIZE(gp_events);
>> +
>> + apic_write(APIC_LVTPC, PC_VECTOR);
>> +
>> + check_gp_counters();
>> + check_rdpmc();
>> + check_counters_many();
>> + check_counter_overflow();
>> + check_gp_counter_cmask();
>> +
>> + return report_summary();
>> +}
>> diff --git a/x86/unittests.cfg b/x86/unittests.cfg
>> index 74156fa..98fb3e9 100644
>> --- a/x86/unittests.cfg
>> +++ b/x86/unittests.cfg
>> @@ -145,6 +145,10 @@ file = intel_pmu.flat
>> extra_params = -cpu host
>> check = /proc/sys/kernel/nmi_watchdog=0
>>
>> +[amd_pmu]
>> +file = amd_pmu.flat
>> +extra_params = -cpu host
>
> maybe add 'arch = x86_64', since AMD only supports 64 bit.
>
>> +
>> [port80]
>> file = port80.flat
>>
>> --
>> 2.7.5
>>
>
> Thanks,
> drew
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2017-08-04 15:49 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-04 4:24 [kvm-unit-tests RFC 0/3] x86/pmu: Add AMD vPMU testing code Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 1/3] x86/pmu: Move common x86 PMU definitions to pmu.h Wei Huang
2017-08-04 7:59 ` Andrew Jones
2017-08-04 4:24 ` [kvm-unit-tests RFC 2/3] x86/pmu: Rename pmu.c code to intel_pmu.c Wei Huang
2017-08-04 8:00 ` Andrew Jones
2017-08-04 15:44 ` Wei Huang
2017-08-04 4:24 ` [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code Wei Huang
2017-08-04 8:14 ` Andrew Jones
2017-08-04 15:49 ` Wei Huang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.