From patchwork Mon Nov 15 16:57:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Elisei X-Patchwork-Id: 12692644 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 30339C433F5 for ; Mon, 15 Nov 2021 17:00:18 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E930561BD2 for ; Mon, 15 Nov 2021 17:00:17 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org E930561BD2 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=arm.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Cc:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=cZyZN3W2HAzmRbXxlHxLUB3sdFltNmjc3LesRFe7mUY=; b=qlXy7bBe7BLQLc LXWlZeAQw/rmqqFD6U/xJfa1DKDmIP2marf9+S0GzWIZi3FeXxPyh/P5RMiN1m2qIApSxKlXdxCop X5ErP4FXUMOJ9YA4Fqa9CUIpzkSpDkb+VfRfvI3m/Jx3RA9njSIgV7XGkWwt5Hnc1Km33n1bql+2t ghWXKGO3FFJxt+ll3UEyi0w04GG1/FWyt5GYG3mtuB0Po4XQKB7iHYsPJRw+0HSguGkde1+0gTPKD s4TmPRz7kzyJp9ztrZ1vVN5WtZ/KiVd1Wcyo8Kl6HL8MbfGDr/StGXj/odBN4IWphv+2lDlxcMoHr TfptmabxzUJvofo1GMGA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mmfJl-00GSGw-FG; Mon, 15 Nov 2021 16:58:53 +0000 Received: from foss.arm.com ([217.140.110.172]) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mmfGg-00GQd1-0z for linux-arm-kernel@lists.infradead.org; Mon, 15 Nov 2021 16:55:43 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6EB131FB; Mon, 15 Nov 2021 08:55:41 -0800 (PST) Received: from monolith.localdoman (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id F15593F766; Mon, 15 Nov 2021 08:55:39 -0800 (PST) From: Alexandru Elisei To: will@kernel.org, julien.thierry.kdev@gmail.com, linux-arm-kernel@lists.infradead.org, kvmarm@lists.cs.columbia.edu, maz@kernel.org, james.morse@arm.com, suzuki.poulose@arm.com, mark.rutland@arm.com Subject: [PATCH kvmtool 9/9] arm64: Add support for KVM_ARM_VCPU_PMU_V3_SET_PMU Date: Mon, 15 Nov 2021 16:57:05 +0000 Message-Id: <20211115165705.195736-10-alexandru.elisei@arm.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115165705.195736-1-alexandru.elisei@arm.com> References: <20211115165705.195736-1-alexandru.elisei@arm.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211115_085542_214706_37E07F29 X-CRM114-Status: GOOD ( 22.59 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The KVM_ARM_VCPU_PMU_V3_CTRL(KVM_ARM_VCPU_PMU_V3_SET_PMU) VCPU ioctl is used to assign a physical PMU to the events that KVM creates when emulating the PMU for that VCPU. This is useful on heterogeneous systems, when there is more than one hardware PMU present. The assumption that is made in the implementation is that the user will pin the kvmtool process on a set of CPUs that share the same PMU. This allows kvmtool to set the same PMU for all VCPUs from the main thread, instead of in the individual VCPU threads. Signed-off-by: Alexandru Elisei --- arm/aarch64/pmu.c | 148 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/arm/aarch64/pmu.c b/arm/aarch64/pmu.c index ac5b7bcd6ca9..c0fc95ca01c4 100644 --- a/arm/aarch64/pmu.c +++ b/arm/aarch64/pmu.c @@ -1,3 +1,9 @@ +#include +#include + +#include "linux/cpumask.h" +#include "linux/err.h" + #include "kvm/fdt.h" #include "kvm/kvm.h" #include "kvm/kvm-cpu.h" @@ -7,6 +13,18 @@ #include "asm/pmu.h" +static bool pmu_has_attr(struct kvm_cpu *vcpu, u64 attr) +{ + struct kvm_device_attr pmu_attr = { + .group = KVM_ARM_VCPU_PMU_V3_CTRL, + .attr = attr, + }; + int ret; + + ret = ioctl(vcpu->vcpu_fd, KVM_HAS_DEVICE_ATTR, &pmu_attr); + return ret == 0; +} + static void set_pmu_attr(struct kvm_cpu *vcpu, void *addr, u64 attr) { struct kvm_device_attr pmu_attr = { @@ -16,8 +34,7 @@ static void set_pmu_attr(struct kvm_cpu *vcpu, void *addr, u64 attr) }; int ret; - ret = ioctl(vcpu->vcpu_fd, KVM_HAS_DEVICE_ATTR, &pmu_attr); - if (!ret) { + if (pmu_has_attr(vcpu, attr)) { ret = ioctl(vcpu->vcpu_fd, KVM_SET_DEVICE_ATTR, &pmu_attr); if (ret) die_perror("PMU KVM_SET_DEVICE_ATTR"); @@ -26,11 +43,126 @@ static void set_pmu_attr(struct kvm_cpu *vcpu, void *addr, u64 attr) } } +#define SYS_EVENT_SOURCE "/sys/bus/event_source/devices/" +/* + * int is 32 bits and INT_MAX translates in decimal to 2 * 10^9. + * Make room for newline and NUL. + */ +#define PMU_ID_MAXLEN 12 + +/* + * In the case of homogeneous systems, there only one hardware PMU, and all + * VCPUs will use the same PMU, regardless of where the attribute gets set. + * + * For heterogeneous systems, the assumption is that the user has pinned the VM + * (via taskset or similar) to a set of CPUs that share the same hardware PMU. + * This simplifies things for kvmtool, as correctness is not affected by setting + * the PMU for each VCPU from the main thread, instead of setting it from each + * individual VCPU thread. + */ +static int find_pmu(void) +{ + char buf[PMU_ID_MAXLEN]; + struct dirent *dirent; + char *cpulist, *path; + int pmu_id = -ENXIO; + unsigned long val; + cpumask_t cpumask; + ssize_t fd_sz; + int this_cpu; + int fd, ret; + DIR *dir; + + memset(buf, 0, PMU_ID_MAXLEN); + + this_cpu = sched_getcpu(); + if (this_cpu < 0) + return -errno; + + cpulist = calloc(1, PAGE_SIZE); + if (!cpulist) + return -ENOMEM; + + path = calloc(1, PAGE_SIZE); + if (!path) { + pmu_id = -ENOMEM; + goto out_free_cpulist; + } + /* Make the compiler happy by copying the NULL terminating byte. */ + strncpy(path, SYS_EVENT_SOURCE, strlen(SYS_EVENT_SOURCE) + 1); + + dir = opendir(SYS_EVENT_SOURCE); + if (!dir) { + pmu_id = -errno; + goto out_free; + } + + while ((dirent = readdir(dir))) { + if (dirent->d_type != DT_LNK) + continue; + + strcat(path, dirent->d_name); + strcat(path, "/cpus"); + fd = open(path, O_RDONLY); + if (fd < 0) + goto next_dir; + + fd_sz = read_file(fd, cpulist, PAGE_SIZE); + if (fd_sz < 0) { + pmu_id = -errno; + goto out_free; + } + close(fd); + + ret = cpulist_parse(cpulist, &cpumask); + if (ret) { + pmu_id = ret; + goto out_free; + } + + if (!cpumask_test_cpu(this_cpu, &cpumask)) + goto next_dir; + + strcpy(&path[strlen(path) - 4], "type"); + fd = open(path, O_RDONLY); + if (fd < 0) + goto next_dir; + + fd_sz = read_file(fd, buf, PMU_ID_MAXLEN - 1); + if (fd_sz < 0) { + pmu_id = -errno; + goto out_free; + } + close(fd); + + val = strtoul(buf, NULL, 10); + if (val > INT_MAX) { + pmu_id = -EOVERFLOW; + goto out_free; + } + pmu_id = (int)val; + pr_debug("Using PMU: %s (id: %d)", dirent->d_name, pmu_id); + break; + +next_dir: + /* Reset path. */ + memset(&path[strlen(SYS_EVENT_SOURCE)], '\0', + strlen(path) - strlen(SYS_EVENT_SOURCE)); + } + +out_free: + free(path); +out_free_cpulist: + free(cpulist); + return pmu_id; +} + void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm) { const char compatible[] = "arm,armv8-pmuv3"; int irq = KVM_ARM_PMUv3_PPI; struct kvm_cpu *vcpu; + int pmu_id = -ENXIO; int i; u32 cpu_mask = (((1 << kvm->nrcpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) \ @@ -44,9 +176,21 @@ void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm) if (!kvm->cfg.arch.has_pmuv3) return; + if (pmu_has_attr(kvm->cpus[0], KVM_ARM_VCPU_PMU_V3_SET_PMU)) { + pmu_id = find_pmu(); + if (pmu_id < 0) + pr_debug("Failed to find a PMU (errno = %d)", -pmu_id); + } + for (i = 0; i < kvm->nrcpus; i++) { vcpu = kvm->cpus[i]; set_pmu_attr(vcpu, &irq, KVM_ARM_VCPU_PMU_V3_IRQ); + /* + * PMU IDs 0-5 are reserved; a positive value means a PMU was + * found. + */ + if (pmu_id > 0) + set_pmu_attr(vcpu, &pmu_id, KVM_ARM_VCPU_PMU_V3_SET_PMU); set_pmu_attr(vcpu, NULL, KVM_ARM_VCPU_PMU_V3_INIT); }