From mboxrd@z Thu Jan 1 00:00:00 1970 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 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 0F248C7EE33 for ; Thu, 26 Jun 2025 21:54:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Type:Cc:To:From: Subject:Message-ID:References:Mime-Version:In-Reply-To:Date:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=mMqYoZsK/epKwgd6CyfTf5pEmCnSON7Fq5dKdiRWdhs=; b=RW426G0B+kW09kQsFTWakOBS4e 4/CLSF3ZB60CZZEg+aElbNAVAny62Bz6ykO2g6pRx+fdavQVarg3DYWxeJ7IikwJBkwwSMLO/0giQ eLkR2GcVH9POiVfhSV1brFfX98R2tAhkC++Gsyzf6TiMu9/xKULT7TOsGfRZRVMgRmLMtFw0tsEOj zjgukJeblOtNqdGaTbGQz96zE5cAz/3CE/ObNREN7RHr33EzH0879/lqZ/Z3qypGr1W/oIsM2Tg0H X17igPqj4RPoivk2fg8sTyKzQNlBuvD5L+rFe0wfUPVahyn1FLrQDj0gwzVbMZz5xxPTjAfWJml0q Nq5tdKzg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1uUuXd-0000000CsWB-3zFA; Thu, 26 Jun 2025 21:53:57 +0000 Received: from mail-il1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1uUsr9-0000000CghM-0ULa for linux-arm-kernel@lists.infradead.org; Thu, 26 Jun 2025 20:06:00 +0000 Received: by mail-il1-x14a.google.com with SMTP id e9e14a558f8ab-3ddba1b53e8so18902685ab.1 for ; Thu, 26 Jun 2025 13:05:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1750968357; x=1751573157; darn=lists.infradead.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=mMqYoZsK/epKwgd6CyfTf5pEmCnSON7Fq5dKdiRWdhs=; b=WPKkgNZkbn31c1sZLzZQgKh1KtUaQruNoY/cYNghvCA1PUKC7NyxcY0vd+r/YA/4Bd mg1NLQXzZAuLmH1uLR7yt0qYu69nYvzVDxsyrdE47nM+K6GXbeNMqCwV+smu5FZqmXNP q0VTbzMbRz1SSg2uK3Vl1++uSv66GkFJa7GutZ2Xngz4f+hJAXD+FTUXmOh7NgefIxeg vBgmTrFVpHMOaTqbYhV3abpZNYHSywCQGGmyFRQBjFP66ts57K/jhS3R7DJQUdpv+2yI XaLA9QmVLDwjVm3Fu6ivDmiDXV8QEQfXH/+gkkxZfTG7c8pY05lMJEAMPnErYyTYLGOG vCbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750968357; x=1751573157; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=mMqYoZsK/epKwgd6CyfTf5pEmCnSON7Fq5dKdiRWdhs=; b=ok8gnQKm7x4CkKVCAUaA9VuaJ0shHev4nH1Y9Q0h7Og24inI/C4lcASHaCeEZ9ZRJl oQcvMjvn/OKBCUley737c1Ntlsh9ECSpN1goEAQ0F2bB5qXW8OMBZCy75tGrTTSyYwZG yIdG4ZGPh8uO9gSDl4t1TMqWGt8yJ2tYCoOaRUJ69FsbPnR1oyQCsh8+ST6lLwe5QSd3 H4WjUXvz3vEKzKpewiw1Gaw+KapcqHH7O7EamaoAqCbO0woJ73LBwENs6WnQRXzWE0Hn d0sgsEEsKzmuBrPMDhUBTGjztyKG1Gmd0CuI0h1NufUSmYSDoheFcvLIr9AzUb1NuZ94 hbPw== X-Forwarded-Encrypted: i=1; AJvYcCXH80+FMG2XMRY41NZRXktLp7eoaEdd6C9TCVqeKmOLwVZVKHSt9+4prRhsfw4Sminb2lERzQNoAv+aySUW4BML@lists.infradead.org X-Gm-Message-State: AOJu0YxGKOXS1a98bGCGSd+3t+daZejdSqoS1/PGaWDQffJfVs+FYb5Q +z9b5ST5KXwVLy1XVXE9EG5lJZbHyKZ3ZiKG7kYQrkpoh0jlzezsq/6sw6YivqXZTVig3PJ6WOI KuaSZ8+txy7imeWpS5+Op7HF7uA== X-Google-Smtp-Source: AGHT+IFdNpatVUTOyeMi8epAV2vW9mg5K1vKFnbAchBnxALDFWUZgDhl9ssvMfuP0TtvfkXE9oLZZ6vS3hSy7zgqdA== X-Received: from ilbee28.prod.google.com ([2002:a05:6e02:491c:b0:3df:31be:c2e5]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a92:cda1:0:b0:3df:2fda:e30b with SMTP id e9e14a558f8ab-3df4accf840mr12363705ab.21.1750968357514; Thu, 26 Jun 2025 13:05:57 -0700 (PDT) Date: Thu, 26 Jun 2025 20:04:42 +0000 In-Reply-To: <20250626200459.1153955-1-coltonlewis@google.com> Mime-Version: 1.0 References: <20250626200459.1153955-1-coltonlewis@google.com> X-Mailer: git-send-email 2.50.0.727.gbf7dc18ff4-goog Message-ID: <20250626200459.1153955-7-coltonlewis@google.com> Subject: [PATCH v3 06/22] perf: arm_pmuv3: Introduce method to partition the PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Paolo Bonzini , Jonathan Corbet , Russell King , Catalin Marinas , Will Deacon , Marc Zyngier , Oliver Upton , Mingwei Zhang , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Mark Rutland , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-perf-users@vger.kernel.org, linux-kselftest@vger.kernel.org, Colton Lewis Content-Type: text/plain; charset="UTF-8" X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250626_130559_194079_3AD5F316 X-CRM114-Status: GOOD ( 31.15 ) 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 For PMUv3, the register field MDCR_EL2.HPMN partitiones the PMU counters into two ranges where counters 0..HPMN-1 are accessible by EL1 and, if allowed, EL0 while counters HPMN..N are only accessible by EL2. Create module parameter reserved_host_counters to reserve a number of counters for the host. This number is set at boot because the perf subsystem assumes the number of counters will not change after the PMU is probed. Introduce the function armv8pmu_partition() to modify the PMU driver's cntr_mask of available counters to exclude the counters being reserved for the guest and record reserved_guest_counters as the maximum allowable value for HPMN. Due to the difficulty this feature would create for the driver running at EL1 on the host, partitioning is only allowed in VHE mode. Working on nVHE mode would require a hypercall for every counter access in the driver because the counters reserved for the host by HPMN are only accessible to EL2. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 14 ++++++ arch/arm64/include/asm/arm_pmuv3.h | 5 ++ arch/arm64/include/asm/kvm_pmu.h | 6 +++ arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/pmu-part.c | 23 ++++++++++ drivers/perf/arm_pmuv3.c | 74 +++++++++++++++++++++++++++++- include/linux/perf/arm_pmu.h | 1 + 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 arch/arm64/kvm/pmu-part.c diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h index 2ec0e5e83fc9..49b1f2d7842d 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -221,6 +221,10 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr) return false; } +static inline bool kvm_pmu_partition_supported(void) +{ + return false; +} static inline bool kvm_set_pmuserenr(u64 val) { return false; @@ -228,6 +232,11 @@ static inline bool kvm_set_pmuserenr(u64 val) static inline void kvm_vcpu_pmu_resync_el0(void) {} +static inline bool has_vhe(void) +{ + return false; +} + /* PMU Version in DFR Register */ #define ARMV8_PMU_DFR_VER_NI 0 #define ARMV8_PMU_DFR_VER_V3P1 0x4 @@ -242,6 +251,11 @@ static inline bool pmuv3_implemented(int pmuver) pmuver == ARMV8_PMU_DFR_VER_NI); } +static inline bool is_pmuv3p1(int pmuver) +{ + return pmuver >= ARMV8_PMU_DFR_VER_V3P1; +} + static inline bool is_pmuv3p4(int pmuver) { return pmuver >= ARMV8_PMU_DFR_VER_V3P4; diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/arm_pmuv3.h index 32c003a7b810..e2057365ba73 100644 --- a/arch/arm64/include/asm/arm_pmuv3.h +++ b/arch/arm64/include/asm/arm_pmuv3.h @@ -173,6 +173,11 @@ static inline bool pmuv3_implemented(int pmuver) pmuver == ID_AA64DFR0_EL1_PMUVer_NI); } +static inline bool is_pmuv3p1(int pmuver) +{ + return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P1; +} + static inline bool is_pmuv3p4(int pmuver) { return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P4; diff --git a/arch/arm64/include/asm/kvm_pmu.h b/arch/arm64/include/asm/kvm_pmu.h index 6c961e877804..8a2ed02e157d 100644 --- a/arch/arm64/include/asm/kvm_pmu.h +++ b/arch/arm64/include/asm/kvm_pmu.h @@ -46,6 +46,7 @@ struct arm_pmu_entry { }; bool kvm_supports_guest_pmuv3(void); +bool kvm_pmu_partition_supported(void); #define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS) u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx); void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val); @@ -115,6 +116,11 @@ static inline bool kvm_supports_guest_pmuv3(void) return false; } +static inline bool kvm_pmu_partition_supported(void) +{ + return false; +} + #define kvm_arm_pmu_irq_initialized(v) (false) static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx) diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 86035b311269..3edbaa57bbf2 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -23,7 +23,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \ vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o -kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o +kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu-part.o pmu.o kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o diff --git a/arch/arm64/kvm/pmu-part.c b/arch/arm64/kvm/pmu-part.c new file mode 100644 index 000000000000..f04c580c19c0 --- /dev/null +++ b/arch/arm64/kvm/pmu-part.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Google LLC + * Author: Colton Lewis + */ + +#include + +#include + +/** + * kvm_pmu_partition_supported() - Determine if partitioning is possible + * + * Partitioning is only supported in VHE mode (with PMUv3, assumed + * since we are in the PMUv3 driver) + * + * Return: True if partitioning is possible, false otherwise + */ +bool kvm_pmu_partition_supported(void) +{ + return kvm_supports_guest_pmuv3() && + has_vhe(); +} diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 3db9f4ed17e8..6358de6c9fab 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -35,6 +35,12 @@ #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_ACCESS 0xEC #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_MISS 0xED +static int reserved_host_counters __read_mostly = -1; + +module_param(reserved_host_counters, int, 0); +MODULE_PARM_DESC(reserved_host_counters, + "PMU Partition: -1 = No partition; +N = Reserve N counters for the host"); + /* * ARMv8 Architectural defined events, not all of these may * be supported on any given implementation. Unsupported events will @@ -500,6 +506,11 @@ static void armv8pmu_pmcr_write(u64 val) write_pmcr(val); } +static u64 armv8pmu_pmcr_n_read(void) +{ + return FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read()); +} + static int armv8pmu_has_overflowed(u64 pmovsr) { return !!(pmovsr & ARMV8_PMU_OVERFLOWED_MASK); @@ -1195,6 +1206,58 @@ struct armv8pmu_probe_info { bool present; }; +/** + * armv8pmu_reservation_is_valid() - Determine if reservation is allowed + * @host_counters: Number of host counters to reserve + * + * Determine if the number of host counters in the argument is an + * allowed reservation, 0 to NR_COUNTERS inclusive. + * + * Return: True if reservation allowed, false otherwise + */ +static bool armv8pmu_reservation_is_valid(int host_counters) +{ + return host_counters >= 0 && + host_counters <= armv8pmu_pmcr_n_read(); +} + +/** + * armv8pmu_partition() - Partition the PMU + * @pmu: Pointer to pmu being partitioned + * @host_counters: Number of host counters to reserve + * + * Partition the given PMU by taking a number of host counters to + * reserve and, if it is a valid reservation, recording the + * corresponding HPMN value in the hpmn_max field of the PMU and + * clearing the guest-reserved counters from the counter mask. + * + * Return: 0 on success, -ERROR otherwise + */ +static int armv8pmu_partition(struct arm_pmu *pmu, int host_counters) +{ + u8 nr_counters; + u8 hpmn; + + if (!armv8pmu_reservation_is_valid(host_counters)) + return -EINVAL; + + nr_counters = armv8pmu_pmcr_n_read(); + hpmn = nr_counters - host_counters; + + pmu->hpmn_max = hpmn; + + bitmap_clear(pmu->cntr_mask, 0, hpmn); + bitmap_set(pmu->cntr_mask, hpmn, host_counters); + clear_bit(ARMV8_PMU_CYCLE_IDX, pmu->cntr_mask); + + if (pmuv3_has_icntr()) + clear_bit(ARMV8_PMU_INSTR_IDX, pmu->cntr_mask); + + pr_info("Partitioned PMU with HPMN %u", hpmn); + + return 0; +} + static void __armv8pmu_probe_pmu(void *info) { struct armv8pmu_probe_info *probe = info; @@ -1209,10 +1272,10 @@ static void __armv8pmu_probe_pmu(void *info) cpu_pmu->pmuver = pmuver; probe->present = true; + cpu_pmu->hpmn_max = -1; /* Read the nb of CNTx counters supported from PMNC */ - bitmap_set(cpu_pmu->cntr_mask, - 0, FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read())); + bitmap_set(cpu_pmu->cntr_mask, 0, armv8pmu_pmcr_n_read()); /* Add the CPU cycles counter */ set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask); @@ -1221,6 +1284,13 @@ static void __armv8pmu_probe_pmu(void *info) if (pmuv3_has_icntr()) set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask); + if (reserved_host_counters >= 0) { + if (kvm_pmu_partition_supported()) + WARN_ON(armv8pmu_partition(cpu_pmu, reserved_host_counters)); + else + pr_err("PMU partition is not supported"); + } + pmceid[0] = pmceid_raw[0] = read_pmceid0(); pmceid[1] = pmceid_raw[1] = read_pmceid1(); diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index fb382bcd4f4b..071cd2d1681c 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -122,6 +122,7 @@ struct arm_pmu { /* Only to be used by ACPI probing code */ unsigned long acpi_cpuid; + int hpmn_max; /* MDCR_EL2.HPMN: counter partition pivot */ }; #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) -- 2.50.0.727.gbf7dc18ff4-goog