From patchwork Fri Aug 4 04:24:30 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Huang X-Patchwork-Id: 9880425 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1C22A603B4 for ; Fri, 4 Aug 2017 04:25:25 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C58228975 for ; Fri, 4 Aug 2017 04:25:25 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0139328990; Fri, 4 Aug 2017 04:25:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5494028975 for ; Fri, 4 Aug 2017 04:25:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751286AbdHDEYj (ORCPT ); Fri, 4 Aug 2017 00:24:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37036 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751273AbdHDEYf (ORCPT ); Fri, 4 Aug 2017 00:24:35 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id BE4E98046A for ; Fri, 4 Aug 2017 04:24:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com BE4E98046A Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=wei@redhat.com Received: from weilaptop.redhat.com (ovpn-120-159.rdu2.redhat.com [10.10.120.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id E70A581E3B; Fri, 4 Aug 2017 04:24:33 +0000 (UTC) From: Wei Huang To: kvm@vger.kernel.org Cc: pbonzini@redhat.com, rkrcmar@redhat.com, wei@redhat.com Subject: [kvm-unit-tests RFC 3/3] x86/pmu: Create AMD PMU test code Date: Thu, 3 Aug 2017 23:24:30 -0500 Message-Id: <1501820670-23194-4-git-send-email-wei@redhat.com> In-Reply-To: <1501820670-23194-1-git-send-email-wei@redhat.com> References: <1501820670-23194-1-git-send-email-wei@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Fri, 04 Aug 2017 04:24:34 +0000 (UTC) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + +#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