From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f73.google.com (mail-oo1-f73.google.com [209.85.161.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55B90346774 for ; Mon, 4 May 2026 21:18:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929512; cv=none; b=tpCOa6aXqEc4Jec4AtsD7IX+EjR+pq7DJYGTXVkK4Y0s2JkrKyC87qX1AdB1nKCOYBP+LG6eKX0v1xyUnhAJdihNB/nszjMLy+TFoy3/rwK87vDFDj2soVX5IQQSRXCHVTdSkltY4+aVb+GHgecwSCnpeJ9BlHdLXWexwVMXHWA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929512; c=relaxed/simple; bh=ISoaF2E6143lYgjhjbBXLJa7G7p7bKVjvejJ6oYVasg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=MNZWe/PdHKPLzCruyevLWT0oSo8JG2qPF0rqvaPTwBVmZvO8HaDktvlDkdhS6ESLx0Ra2VP6ub55jBkVbjBUBigqLAnDH21rm2Ze0cVPG9Sih+2cLq43fjOyKqHFbKR/6AXqPT5YRFWnZJDdh1lnZwuEfO+hZz1qeGaKWkwSS38= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Rmpi2FJG; arc=none smtp.client-ip=209.85.161.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Rmpi2FJG" Received: by mail-oo1-f73.google.com with SMTP id 006d021491bc7-679c5ed0942so1272484eaf.1 for ; Mon, 04 May 2026 14:18:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929509; x=1778534309; darn=vger.kernel.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=2uiiVseT2qkhFwzL7iHG6lxFCIgHI+i9LhCSR+WQs4k=; b=Rmpi2FJGsk7TF+ReF7EuLRuZraMtUW3eWlFeCuyaza/CejAzca1VQ7YwchZfGLi7XB HByWaDLQc7UjzbrSki4YMz6QHvvGmqTKqay2pxui3UD+GoQ2ve+N/S7Qy+yo6iPYMvpT dVKR2oONxuQFzzCSkSqYdUYfZ0IT5lrjsuQWYRnYLtNJVhEQ4a8C4Hr42dAPeVHefW6b 6bnp00P0h13u+wRi/9MKqErhT5LZ6zXNvDA2OHATzJAdxUyUN8uF+TuKYdB9cztbKyvs CqDrzG0BcXor20TOnNq62kiR2AcppSQ+LELyG2sPgV6kxGfDK1QC5hgNRv8FMlfQNEmU KWtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929509; x=1778534309; 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=2uiiVseT2qkhFwzL7iHG6lxFCIgHI+i9LhCSR+WQs4k=; b=nPD2pV+Z306OxqRUFQcaJAL+RUNK0K6CBBGVNmevlEvVgEoNlS1m/6dYixIgiLxruL /ZPErEHVf2se36L/Y0s4Es1PVVlav8UjviYn5O8bLpYJ+4ZcrXCP5e7dduXwyXMvCLCU JNLxj04RbK5ZO5pHj5l8pW86URHLrsnV8WjXfFam9CJikAOOYUVpzkejgy/VnSA1R/yX 0vMxKowk/SNAAuQ4ko1ygdrPDlv8hnns5sQtOVtGEs4i85JbgCqgvQh96QtPXZETLQ0C T8Sylg0rKGI7kgGDKiTn163xo88lXXOO69fB0fa6cpA0jLnYj50sSDANTbad/57KX+d3 qyhw== X-Forwarded-Encrypted: i=1; AFNElJ/g1ccBXI+N+VZFQ1yxIlPpDK+FXIWNFOq1rhFUlzr0bi3KHng3d7EdPwB7XSXOVJWLAuIkpB0zOjLUvBc=@vger.kernel.org X-Gm-Message-State: AOJu0YzZHFA09mU7NWy/Q12MM/Hr7E6Tdwc6bttK1XjKHV4z7LR00/lw JEC76Z2ZTbvT6jB/ohC/Z4CsgrEgzpnwM3+hDx606YTgROjqjjq2C6vAGGYkP/TUmFdRMpss2Th m7L9Lj6NyssU5xdgvX/CZVIolSQ== X-Received: from oalw10.prod.google.com ([2002:a05:6870:948a:b0:41c:55f7:96a3]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2223:b0:694:8d83:a344 with SMTP id 006d021491bc7-696979f0660mr6109852eaf.16.1777929509296; Mon, 04 May 2026 14:18:29 -0700 (PDT) Date: Mon, 4 May 2026 21:17:54 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-2-coltonlewis@google.com> Subject: [PATCH v7 01/20] arm64: cpufeature: Add cpucap for HPMN0 From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a capability for FEAT_HPMN0, whether MDCR_EL2.HPMN can specify 0 counters reserved for the guest. This required changing HPMN0 to an UnsignedEnum in tools/sysreg because otherwise not all the appropriate macros are generated to add it to arm64_cpu_capabilities_arm64_features. Acked-by: Mark Rutland Reviewed-by: Suzuki K Poulose Signed-off-by: Colton Lewis --- arch/arm64/kernel/cpufeature.c | 8 ++++++++ arch/arm64/kvm/sys_regs.c | 3 ++- arch/arm64/tools/cpucaps | 1 + arch/arm64/tools/sysreg | 6 +++--- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 32c2dbcc0c641..5c6c76a9696cc 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -556,6 +556,7 @@ static const struct arm64_ftr_bits ftr_id_mmfr0[] =3D { }; =20 static const struct arm64_ftr_bits ftr_id_aa64dfr0[] =3D { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_EL1= _HPMN0_SHIFT, 4, 0), S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_EL1_= DoubleLock_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_EL1= _PMSVer_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_EL1_CT= X_CMPs_SHIFT, 4, 0), @@ -2964,6 +2965,13 @@ static const struct arm64_cpu_capabilities arm64_fea= tures[] =3D { .matches =3D has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64MMFR0_EL1, FGT, FGT2) }, + { + .desc =3D "HPMN0", + .type =3D ARM64_CPUCAP_SYSTEM_FEATURE, + .capability =3D ARM64_HAS_HPMN0, + .matches =3D has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64DFR0_EL1, HPMN0, IMP) + }, #ifdef CONFIG_ARM64_SME { .desc =3D "Scalable Matrix Extension", diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 1b4cacb6e918a..0a8e8ee69cd00 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -3232,7 +3232,8 @@ static const struct sys_reg_desc sys_reg_descs[] =3D { ID_AA64DFR0_EL1_DoubleLock_MASK | ID_AA64DFR0_EL1_WRPs_MASK | ID_AA64DFR0_EL1_PMUVer_MASK | - ID_AA64DFR0_EL1_DebugVer_MASK), + ID_AA64DFR0_EL1_DebugVer_MASK | + ID_AA64DFR0_EL1_HPMN0_MASK), ID_SANITISED(ID_AA64DFR1_EL1), ID_UNALLOCATED(5,2), ID_UNALLOCATED(5,3), diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 7261553b644b2..654b165781854 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -42,6 +42,7 @@ HAS_GIC_PRIO_MASKING HAS_GIC_PRIO_RELAXED_SYNC HAS_ICH_HCR_EL2_TDIR HAS_HCR_NV1 +HAS_HPMN0 HAS_HCX HAS_LDAPR HAS_LPA2 diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 9d1c211080571..92135f8834be0 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1666,9 +1666,9 @@ EndEnum EndSysreg =20 Sysreg ID_AA64DFR0_EL1 3 0 0 5 0 -Enum 63:60 HPMN0 - 0b0000 UNPREDICTABLE - 0b0001 DEF +UnsignedEnum 63:60 HPMN0 + 0b0000 NI + 0b0001 IMP EndEnum UnsignedEnum 59:56 ExtTrcBuff 0b0000 NI --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f73.google.com (mail-oo1-f73.google.com [209.85.161.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B7799346E68 for ; Mon, 4 May 2026 21:18:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929513; cv=none; b=Us3pCo9oFBvAv2cIY6n0beYh/IcLBsN/TCtqSr2IfHqM7xBqrHlcPm/g8uP8jH29Gx2mT1lKNVz2rrTimwNb2mfp7c7bcHUhlbdyZ8y7A1ZRdCjwQT1WoVwxqXXRHyNhL02BcZuCDwmSiPjno/Oxz5eGsUZbkoWGoJToh+h373I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929513; c=relaxed/simple; bh=QnWGvE5r/VD50+dB5BY+tSgcU1JPBszfEEBcsQx8zVU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=GRhT+bjrpKJ+kfxhXoZN1JUkKWEgFswWBbv5txZXvUUYt8eGO5w1aMPlv6b36Jx0M+ywgl5SF859HJ2efz6qFcNPthBBOcmCO0FIyuaEHsQrRFVvQH9ldYfBPiHmpoOWlYIouQHkzpS7TQJuFPYQWKN4KIBD0ZBNg5rbQRHmbzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=mxLfHn20; arc=none smtp.client-ip=209.85.161.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mxLfHn20" Received: by mail-oo1-f73.google.com with SMTP id 006d021491bc7-6961bda4505so5608046eaf.3 for ; Mon, 04 May 2026 14:18:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929511; x=1778534311; darn=vger.kernel.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=mx9MuEs10AivytOpsddviHcuIhm9po4u8KpiL1CkIVk=; b=mxLfHn20LqZprrtoPcLEGCHVC1mldaELnvJnAeXzkdrZkVy2WGhjlpbhymAGwMs4CV 4NDJtgMavxa8IdoVVnjLQtwHFPL8lOZ3TKzMl69JYNr+fKdHFfogqW+zfKDoCmQPKVJX 6uU9zGFQOQgT/5yoRB4j1TMaiYHsVbInaLBrWoiCMdPT6Voc2WfJROcz4Ne4rOeCPPy7 TvSNgPyOtoUmZEsGdP2E0MMzouZ5/EERJJ8V2EJisJgo1biBeVKEglYTUBaEd2NZLNwj 56ApH6l/9C+vp/bKLr6bBvqTHfoJAV74PkUe2As/xU1Z/RAzbfj3hdIPQch8lqai/bl0 Ry2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929511; x=1778534311; 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=mx9MuEs10AivytOpsddviHcuIhm9po4u8KpiL1CkIVk=; b=kyHTJofpvOOodnBlhwC8TpIxDDM+7ODKNYbyUYUITaeXo3JewCNWwzu4VBccbJe6Fz bBh8jur3XfAS+tbJMGSX81kojspEc77xET+ymLgsUV30B5/7UD47X/ACU2r/RKoP/wYO WJyifbUlc5/pC7IdxOPln7JIIhPLehQGUpMEjK93t7tUzf6SOWM2T/0u5RuDADYEnWx1 9KoLImVVWqqHdxKHLp9xnjTV+n2GX/Xj6SX5wntO/VDLWqZEDJazYkdXZ+GK2lrpQAtR udgrmJ95XyWX0vWAC8g61AHoPlO9g8ZRSDsIbHEnOso5j0E5LMxBWB4BurZg5QKUA6sf odJQ== X-Forwarded-Encrypted: i=1; AFNElJ88eq00XZjMaIEvPqmpYXe5Xt3m2NaMZrwgWnd6GfsVx6d9s4cUrM3c7x5XPIqqrLXPD7EZ9+WlwIaPzjc=@vger.kernel.org X-Gm-Message-State: AOJu0Yx/8p614IWZKTa1HbFhQ4ofHKyM4QHbDB1GHskFz/YMpiTiXh9U HQSS+KCy62jLrr1Zq2IKwPJsLkkiR1BdpQpC03XQMslc/SJv97uUY8IDFnXeKobBz6dh/xcIVxX jCOV6FYd87GBrP+Vi03zkRVDjqA== X-Received: from jams10.prod.google.com ([2002:a02:c50a:0:b0:5d9:a04c:991d]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:179b:b0:696:1814:717a with SMTP id 006d021491bc7-69697dc2ac6mr5287197eaf.47.1777929510525; Mon, 04 May 2026 14:18:30 -0700 (PDT) Date: Mon, 4 May 2026 21:17:55 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-3-coltonlewis@google.com> Subject: [PATCH v7 02/20] KVM: arm64: Reorganize PMU includes From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Marc Zyngier Including *all* of asm/kvm_host.h in asm/arm_pmuv3.h is a bad idea because that is much more than arm_pmuv3.h logically needs and creates a circular dependency that makes it easy to introduce compiler errors when editing this code. asm/kvm_host.h includes kvm/arm_pmu.h includes perf/arm_pmuv3.h includes asm/arm_pmuv3.h includes asm/kvm_host.h Reorganize the PMU includes to be more sane. In particular: * Remove the circular dependency by removing the kvm_host.h include from asm/arm_pmuv3.h since 99% of it isn't needed. * Move the remaining tiny bit of KVM/PMU interface from kvm_host.h into arm_pmu.h * Conditionally on ARM64, include the more targeted arm_pmu.h directly in the arm_pmuv3.c driver. Signed-off-by: Marc Zyngier Signed-off-by: Colton Lewis --- arch/arm64/include/asm/arm_pmuv3.h | 2 -- arch/arm64/include/asm/kvm_host.h | 14 -------------- drivers/perf/arm_pmuv3.c | 5 +++++ include/kvm/arm_pmu.h | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/ar= m_pmuv3.h index 8a777dec8d88a..cf2b2212e00a2 100644 --- a/arch/arm64/include/asm/arm_pmuv3.h +++ b/arch/arm64/include/asm/arm_pmuv3.h @@ -6,8 +6,6 @@ #ifndef __ASM_PMUV3_H #define __ASM_PMUV3_H =20 -#include - #include #include =20 diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 70cb9cfd760a3..1f789ba589d56 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1441,25 +1441,11 @@ void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcp= u); void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu); =20 -static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr) -{ - return (!has_vhe() && attr->exclude_host); -} - #ifdef CONFIG_KVM -void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr); -void kvm_clr_pmu_events(u64 clr); -bool kvm_set_pmuserenr(u64 val); void kvm_enable_trbe(void); void kvm_disable_trbe(void); void kvm_tracing_set_el1_configuration(u64 trfcr_while_in_guest); #else -static inline void kvm_set_pmu_events(u64 set, struct perf_event_attr *att= r) {} -static inline void kvm_clr_pmu_events(u64 clr) {} -static inline bool kvm_set_pmuserenr(u64 val) -{ - return false; -} static inline void kvm_enable_trbe(void) {} static inline void kvm_disable_trbe(void) {} static inline void kvm_tracing_set_el1_configuration(u64 trfcr_while_in_gu= est) {} diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 8014ff766cff5..8d3b832cd633a 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -9,6 +9,11 @@ */ =20 #include + +#if defined(CONFIG_ARM64) +#include +#endif + #include #include =20 diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 96754b51b4116..e91d15a7a564b 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -9,9 +9,19 @@ =20 #include #include +#include =20 #define KVM_ARMV8_PMU_MAX_COUNTERS 32 =20 +#define kvm_pmu_counter_deferred(attr) \ + ({ \ + !has_vhe() && (attr)->exclude_host; \ + }) + +struct kvm; +struct kvm_device_attr; +struct kvm_vcpu; + #if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM) struct kvm_pmc { u8 idx; /* index into the pmu->pmc array */ @@ -66,6 +76,9 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu); =20 struct kvm_pmu_events *kvm_get_pmu_events(void); +void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr); +void kvm_clr_pmu_events(u64 clr); +bool kvm_set_pmuserenr(u64 val); void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu); void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu); void kvm_vcpu_pmu_resync_el0(void); @@ -159,6 +172,12 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *= vcpu, bool pmceid1) =20 #define kvm_vcpu_has_pmu(vcpu) ({ false; }) static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {} +static inline void kvm_set_pmu_events(u64 set, struct perf_event_attr *att= r) {} +static inline void kvm_clr_pmu_events(u64 clr) {} +static inline bool kvm_set_pmuserenr(u64 val) +{ + return false; +} static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {} static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {} static inline void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) {} --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f74.google.com (mail-ot1-f74.google.com [209.85.210.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D8DD1347533 for ; Mon, 4 May 2026 21:18:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; cv=none; b=SkQfYUn6YMzBiAq/IqjgP8PSQivJcG3RKKhGRLcdB2T8g1shZl4g0KZ4S23Y3zT7iBflh4MET3l6L15p0jQVCm9IzJye9qKYNO02riFEsAAK6VBtHkSni1ZTTJ/OvByn31iAKeaycMtOgbDuaW8jyz1/Thcsv3vYhsIez9HhRjo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; c=relaxed/simple; bh=3VXbaBHjrRATdQbRd3uTHfvaeyDlvlbwg/KIFYawY0s=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=VZoGR/0YO1fFVazWZ6s3VJJISkVw4nWGrohmA+AMFl/3qT+/ma3/YK09bWEJOFy++xIZpBf/khMkKDjXNfHHu3V2ImQPVSVFNtnDP30bmGw2T9Wd0q8hId+FOH+PjRgp2189OqDCRKMnZ+S27UWKG0XH8t54LKpLCNR3NEamrgc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=N7vSS2bj; arc=none smtp.client-ip=209.85.210.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="N7vSS2bj" Received: by mail-ot1-f74.google.com with SMTP id 46e09a7af769-7dcdcacd141so6965023a34.2 for ; Mon, 04 May 2026 14:18:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929512; x=1778534312; darn=vger.kernel.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=+LGItt7/MlFONg4dt0SCJIQkgfsTmQQ+GbsaSA/mJQY=; b=N7vSS2bjj0gglMuso6HgR0Dj85LUod7kYDRkeowmjA7JnmCx8cJQPnvsk73sRY/p7f qVHrVIvj2hUdglE3FiU6L8/FolxVg8ATxUjKiJoek2dhAXHTeA0L+zgKWuYpG3GoHpUV iyEWfdsHa/57+2To6ackC+C/G4FJBuLyfL2kuFTgxcbSQi5QY8nfcwdScuX38rcdQOoG cnRVJSsOE71eMjCpBc4CCufDtjGKubmseoaDKEoyT3bDEDLGZ1/Ft22IF+bOlokYHSpu SNLMXVZkA68nU4CUufkVGokLUmiAPzYnsKYxmvt24JvAHO5JnMRaO8sgcOJHPo2AIJbz lH9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929512; x=1778534312; 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=+LGItt7/MlFONg4dt0SCJIQkgfsTmQQ+GbsaSA/mJQY=; b=IPgVuEpOmgqhttmQwkK+FrsT/I7bKcHoBOpb4yREW/LgHqyHwPsr2meeOzeDKWi2Ux 48wKHwEFIiWsJ56YfHcAQ8YMzY5KJHUzcIM+S3nrW1teD0G9SHbrEuWF9xORGMGtl1mV ThcbLPQVUghqVKqHmEKZ2E8A8TYK90Lccl3/1+r//phYMVGK7mlY1/pZgjEEwuXRORdQ FIlt0VzDBXPugvsOLbl7hpDMV+YRtyNeaLSKdUyELkuR90OFyGJhS1b0EqWs1dHZz3z0 bJmqkJCCjGprlxlPoD8T78P/ZdcLQxAwv8HMrnkCrflN6qR9Zt2qld0YTYCBifwhs+/N RJBg== X-Forwarded-Encrypted: i=1; AFNElJ+ybR6yOt67Mu0jMxrjXF/r6SbWZqHjZPu45zNTNklSvOkNdNQp4QK9zBe3WbpTun2NnpDOgcxorN6IRVI=@vger.kernel.org X-Gm-Message-State: AOJu0Ywquf8gTuCux08ZsxTCUJGpQ2GkJhAmzN0oTbbqdH+V1cE0gOoi mFqJGTp7Lw0N9DHXla8UAUDL1CeqszRDtnGgAMUba99aCfGr2vDcJH3T2PnFIVQzrgyTL+/0vP/ yPmszt+AeGmbaC8CMMsLRLip8+g== X-Received: from oaqf40.prod.google.com ([2002:a05:6870:89a8:b0:423:d3c:6399]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:160c:b0:694:8cd5:10c9 with SMTP id 006d021491bc7-6998d4626c3mr145947eaf.51.1777929511621; Mon, 04 May 2026 14:18:31 -0700 (PDT) Date: Mon, 4 May 2026 21:17:56 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-4-coltonlewis@google.com> Subject: [PATCH v7 03/20] KVM: arm64: Reorganize PMU functions From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A lot of functions in pmu-emul.c aren't specific to the emulated PMU implementation. Move them to the more appropriate pmu.c file where shared PMU functions should live. Signed-off-by: Colton Lewis --- arch/arm64/kvm/pmu-emul.c | 672 +------------------------------------ arch/arm64/kvm/pmu.c | 676 ++++++++++++++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 7 + 3 files changed, 684 insertions(+), 671 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 93cc9bbb5cecd..a40db0d5120ff 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -17,19 +17,10 @@ =20 #define PERF_ATTR_CFG1_COUNTER_64BIT BIT(0) =20 -static LIST_HEAD(arm_pmus); -static DEFINE_MUTEX(arm_pmus_lock); - static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc); static void kvm_pmu_release_perf_event(struct kvm_pmc *pmc); static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc); =20 -bool kvm_supports_guest_pmuv3(void) -{ - guard(mutex)(&arm_pmus_lock); - return !list_empty(&arm_pmus); -} - static struct kvm_vcpu *kvm_pmc_to_vcpu(const struct kvm_pmc *pmc) { return container_of(pmc, struct kvm_vcpu, arch.pmu.pmc[pmc->idx]); @@ -40,46 +31,6 @@ static struct kvm_pmc *kvm_vcpu_idx_to_pmc(struct kvm_vc= pu *vcpu, int cnt_idx) return &vcpu->arch.pmu.pmc[cnt_idx]; } =20 -static u32 __kvm_pmu_event_mask(unsigned int pmuver) -{ - switch (pmuver) { - case ID_AA64DFR0_EL1_PMUVer_IMP: - return GENMASK(9, 0); - case ID_AA64DFR0_EL1_PMUVer_V3P1: - case ID_AA64DFR0_EL1_PMUVer_V3P4: - case ID_AA64DFR0_EL1_PMUVer_V3P5: - case ID_AA64DFR0_EL1_PMUVer_V3P7: - return GENMASK(15, 0); - default: /* Shouldn't be here, just for sanity */ - WARN_ONCE(1, "Unknown PMU version %d\n", pmuver); - return 0; - } -} - -static u32 kvm_pmu_event_mask(struct kvm *kvm) -{ - u64 dfr0 =3D kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1); - u8 pmuver =3D SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0); - - return __kvm_pmu_event_mask(pmuver); -} - -u64 kvm_pmu_evtyper_mask(struct kvm *kvm) -{ - u64 mask =3D ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 | - kvm_pmu_event_mask(kvm); - - if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL2, IMP)) - mask |=3D ARMV8_PMU_INCLUDE_EL2; - - if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP)) - mask |=3D ARMV8_PMU_EXCLUDE_NS_EL0 | - ARMV8_PMU_EXCLUDE_NS_EL1 | - ARMV8_PMU_EXCLUDE_EL3; - - return mask; -} - /** * kvm_pmc_is_64bit - determine if counter is 64bit * @pmc: counter context @@ -272,59 +223,6 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu) irq_work_sync(&vcpu->arch.pmu.overflow_work); } =20 -static u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu) -{ - unsigned int hpmn, n; - - if (!vcpu_has_nv(vcpu)) - return 0; - - hpmn =3D SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2)); - n =3D vcpu->kvm->arch.nr_pmu_counters; - - /* - * Programming HPMN to a value greater than PMCR_EL0.N is - * CONSTRAINED UNPREDICTABLE. Make the implementation choice that an - * UNKNOWN number of counters (in our case, zero) are reserved for EL2. - */ - if (hpmn >=3D n) - return 0; - - /* - * Programming HPMN=3D0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't - * implemented. Since KVM's ability to emulate HPMN=3D0 does not directly - * depend on hardware (all PMU registers are trapped), make the - * implementation choice that all counters are included in the second - * range reserved for EL2/EL3. - */ - return GENMASK(n - 1, hpmn); -} - -bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int idx) -{ - return kvm_pmu_hyp_counter_mask(vcpu) & BIT(idx); -} - -u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu) -{ - u64 mask =3D kvm_pmu_implemented_counter_mask(vcpu); - - if (!vcpu_has_nv(vcpu) || vcpu_is_el2(vcpu)) - return mask; - - return mask & ~kvm_pmu_hyp_counter_mask(vcpu); -} - -u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu) -{ - u64 val =3D FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu)); - - if (val =3D=3D 0) - return BIT(ARMV8_PMU_CYCLE_IDX); - else - return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX); -} - static void kvm_pmc_enable_perf_event(struct kvm_pmc *pmc) { if (!pmc->perf_event) { @@ -370,7 +268,7 @@ void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vc= pu, u64 val) * counter where the values of the global enable control, PMOVSSET_EL0[n],= and * PMINTENSET_EL1[n] are all 1. */ -static bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) +bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) { u64 reg =3D __vcpu_sys_reg(vcpu, PMOVSSET_EL0); =20 @@ -393,24 +291,6 @@ static bool kvm_pmu_overflow_status(struct kvm_vcpu *v= cpu) return reg; } =20 -static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) -{ - struct kvm_pmu *pmu =3D &vcpu->arch.pmu; - bool overflow; - - overflow =3D kvm_pmu_overflow_status(vcpu); - if (pmu->irq_level =3D=3D overflow) - return; - - pmu->irq_level =3D overflow; - - if (likely(irqchip_in_kernel(vcpu->kvm))) { - int ret =3D kvm_vgic_inject_irq(vcpu->kvm, vcpu, - pmu->irq_num, overflow, pmu); - WARN_ON(ret); - } -} - bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu) { struct kvm_pmu *pmu =3D &vcpu->arch.pmu; @@ -436,43 +316,6 @@ void kvm_pmu_update_run(struct kvm_vcpu *vcpu) regs->device_irq_level |=3D KVM_ARM_DEV_PMU; } =20 -/** - * kvm_pmu_flush_hwstate - flush pmu state to cpu - * @vcpu: The vcpu pointer - * - * Check if the PMU has overflowed while we were running in the host, and = inject - * an interrupt if that was the case. - */ -void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu) -{ - kvm_pmu_update_state(vcpu); -} - -/** - * kvm_pmu_sync_hwstate - sync pmu state from cpu - * @vcpu: The vcpu pointer - * - * Check if the PMU has overflowed while we were running in the guest, and - * inject an interrupt if that was the case. - */ -void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu) -{ - kvm_pmu_update_state(vcpu); -} - -/* - * When perf interrupt is an NMI, we cannot safely notify the vcpu corresp= onding - * to the event. - * This is why we need a callback to do it once outside of the NMI context. - */ -static void kvm_pmu_perf_overflow_notify_vcpu(struct irq_work *work) -{ - struct kvm_vcpu *vcpu; - - vcpu =3D container_of(work, struct kvm_vcpu, arch.pmu.overflow_work); - kvm_vcpu_kick(vcpu); -} - /* * Perform an increment on any of the counters described in @mask, * generating the overflow if required, and propagate it as a chained @@ -784,132 +627,6 @@ void kvm_pmu_set_counter_event_type(struct kvm_vcpu *= vcpu, u64 data, kvm_pmu_create_perf_event(pmc); } =20 -void kvm_host_pmu_init(struct arm_pmu *pmu) -{ - struct arm_pmu_entry *entry; - - /* - * Check the sanitised PMU version for the system, as KVM does not - * support implementations where PMUv3 exists on a subset of CPUs. - */ - if (!pmuv3_implemented(kvm_arm_pmu_get_pmuver_limit())) - return; - - guard(mutex)(&arm_pmus_lock); - - entry =3D kmalloc_obj(*entry); - if (!entry) - return; - - entry->arm_pmu =3D pmu; - list_add_tail(&entry->entry, &arm_pmus); -} - -static struct arm_pmu *kvm_pmu_probe_armpmu(void) -{ - struct arm_pmu_entry *entry; - struct arm_pmu *pmu; - int cpu; - - guard(mutex)(&arm_pmus_lock); - - /* - * It is safe to use a stale cpu to iterate the list of PMUs so long as - * the same value is used for the entirety of the loop. Given this, and - * the fact that no percpu data is used for the lookup there is no need - * to disable preemption. - * - * It is still necessary to get a valid cpu, though, to probe for the - * default PMU instance as userspace is not required to specify a PMU - * type. In order to uphold the preexisting behavior KVM selects the - * PMU instance for the core during vcpu init. A dependent use - * case would be a user with disdain of all things big.LITTLE that - * affines the VMM to a particular cluster of cores. - * - * In any case, userspace should just do the sane thing and use the UAPI - * to select a PMU type directly. But, be wary of the baggage being - * carried here. - */ - cpu =3D raw_smp_processor_id(); - list_for_each_entry(entry, &arm_pmus, entry) { - pmu =3D entry->arm_pmu; - - if (cpumask_test_cpu(cpu, &pmu->supported_cpus)) - return pmu; - } - - return NULL; -} - -static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1) -{ - u32 hi[2], lo[2]; - - bitmap_to_arr32(lo, pmu->pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS); - bitmap_to_arr32(hi, pmu->pmceid_ext_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS= ); - - return ((u64)hi[pmceid1] << 32) | lo[pmceid1]; -} - -static u64 compute_pmceid0(struct arm_pmu *pmu) -{ - u64 val =3D __compute_pmceid(pmu, 0); - - /* always support SW_INCR */ - val |=3D BIT(ARMV8_PMUV3_PERFCTR_SW_INCR); - /* always support CHAIN */ - val |=3D BIT(ARMV8_PMUV3_PERFCTR_CHAIN); - return val; -} - -static u64 compute_pmceid1(struct arm_pmu *pmu) -{ - u64 val =3D __compute_pmceid(pmu, 1); - - /* - * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled - * as RAZ - */ - val &=3D ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) | - BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) | - BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32)); - return val; -} - -u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1) -{ - struct arm_pmu *cpu_pmu =3D vcpu->kvm->arch.arm_pmu; - unsigned long *bmap =3D vcpu->kvm->arch.pmu_filter; - u64 val, mask =3D 0; - int base, i, nr_events; - - if (!pmceid1) { - val =3D compute_pmceid0(cpu_pmu); - base =3D 0; - } else { - val =3D compute_pmceid1(cpu_pmu); - base =3D 32; - } - - if (!bmap) - return val; - - nr_events =3D kvm_pmu_event_mask(vcpu->kvm) + 1; - - for (i =3D 0; i < 32; i +=3D 8) { - u64 byte; - - byte =3D bitmap_get_value8(bmap, base + i); - mask |=3D byte << i; - if (nr_events >=3D (0x4000 + base + 32)) { - byte =3D bitmap_get_value8(bmap, 0x4000 + base + i); - mask |=3D byte << (32 + i); - } - } - - return val & mask; -} - void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) { u64 mask =3D kvm_pmu_implemented_counter_mask(vcpu); @@ -921,393 +638,6 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu) kvm_pmu_reprogram_counter_mask(vcpu, mask); } =20 -int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu) -{ - if (!vcpu->arch.pmu.created) - return -EINVAL; - - /* - * A valid interrupt configuration for the PMU is either to have a - * properly configured interrupt number and using an in-kernel - * irqchip, or to not have an in-kernel GIC and not set an IRQ. - */ - if (irqchip_in_kernel(vcpu->kvm)) { - int irq =3D vcpu->arch.pmu.irq_num; - /* - * If we are using an in-kernel vgic, at this point we know - * the vgic will be initialized, so we can check the PMU irq - * number against the dimensions of the vgic and make sure - * it's valid. - */ - if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq)) - return -EINVAL; - } else if (kvm_arm_pmu_irq_initialized(vcpu)) { - return -EINVAL; - } - - return 0; -} - -static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu) -{ - if (irqchip_in_kernel(vcpu->kvm)) { - int ret; - - /* - * If using the PMU with an in-kernel virtual GIC - * implementation, we require the GIC to be already - * initialized when initializing the PMU. - */ - if (!vgic_initialized(vcpu->kvm)) - return -ENODEV; - - if (!kvm_arm_pmu_irq_initialized(vcpu)) - return -ENXIO; - - ret =3D kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num, - &vcpu->arch.pmu); - if (ret) - return ret; - } - - init_irq_work(&vcpu->arch.pmu.overflow_work, - kvm_pmu_perf_overflow_notify_vcpu); - - vcpu->arch.pmu.created =3D true; - return 0; -} - -/* - * For one VM the interrupt type must be same for each vcpu. - * As a PPI, the interrupt number is the same for all vcpus, - * while as an SPI it must be a separate number per vcpu. - */ -static bool pmu_irq_is_valid(struct kvm *kvm, int irq) -{ - unsigned long i; - struct kvm_vcpu *vcpu; - - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!kvm_arm_pmu_irq_initialized(vcpu)) - continue; - - if (irq_is_ppi(irq)) { - if (vcpu->arch.pmu.irq_num !=3D irq) - return false; - } else { - if (vcpu->arch.pmu.irq_num =3D=3D irq) - return false; - } - } - - return true; -} - -/** - * kvm_arm_pmu_get_max_counters - Return the max number of PMU counters. - * @kvm: The kvm pointer - */ -u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm) -{ - struct arm_pmu *arm_pmu =3D kvm->arch.arm_pmu; - - /* - * PMUv3 requires that all event counters are capable of counting any - * event, though the same may not be true of non-PMUv3 hardware. - */ - if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) - return 1; - - /* - * The arm_pmu->cntr_mask considers the fixed counter(s) as well. - * Ignore those and return only the general-purpose counters. - */ - return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS); -} - -static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr) -{ - kvm->arch.nr_pmu_counters =3D nr; - - /* Reset MDCR_EL2.HPMN behind the vcpus' back... */ - if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features)) { - struct kvm_vcpu *vcpu; - unsigned long i; - - kvm_for_each_vcpu(i, vcpu, kvm) { - u64 val =3D __vcpu_sys_reg(vcpu, MDCR_EL2); - val &=3D ~MDCR_EL2_HPMN; - val |=3D FIELD_PREP(MDCR_EL2_HPMN, kvm->arch.nr_pmu_counters); - __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val); - } - } -} - -static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu) -{ - lockdep_assert_held(&kvm->arch.config_lock); - - kvm->arch.arm_pmu =3D arm_pmu; - kvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm)); -} - -/** - * kvm_arm_set_default_pmu - No PMU set, get the default one. - * @kvm: The kvm pointer - * - * The observant among you will notice that the supported_cpus - * mask does not get updated for the default PMU even though it - * is quite possible the selected instance supports only a - * subset of cores in the system. This is intentional, and - * upholds the preexisting behavior on heterogeneous systems - * where vCPUs can be scheduled on any core but the guest - * counters could stop working. - */ -int kvm_arm_set_default_pmu(struct kvm *kvm) -{ - struct arm_pmu *arm_pmu =3D kvm_pmu_probe_armpmu(); - - if (!arm_pmu) - return -ENODEV; - - kvm_arm_set_pmu(kvm, arm_pmu); - return 0; -} - -static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id) -{ - struct kvm *kvm =3D vcpu->kvm; - struct arm_pmu_entry *entry; - struct arm_pmu *arm_pmu; - int ret =3D -ENXIO; - - lockdep_assert_held(&kvm->arch.config_lock); - mutex_lock(&arm_pmus_lock); - - list_for_each_entry(entry, &arm_pmus, entry) { - arm_pmu =3D entry->arm_pmu; - if (arm_pmu->pmu.type =3D=3D pmu_id) { - if (kvm_vm_has_ran_once(kvm) || - (kvm->arch.pmu_filter && kvm->arch.arm_pmu !=3D arm_pmu)) { - ret =3D -EBUSY; - break; - } - - kvm_arm_set_pmu(kvm, arm_pmu); - cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus); - ret =3D 0; - break; - } - } - - mutex_unlock(&arm_pmus_lock); - return ret; -} - -static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned = int n) -{ - struct kvm *kvm =3D vcpu->kvm; - - if (!kvm->arch.arm_pmu) - return -EINVAL; - - if (n > kvm_arm_pmu_get_max_counters(kvm)) - return -EINVAL; - - kvm_arm_set_nr_counters(kvm, n); - return 0; -} - -int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) -{ - struct kvm *kvm =3D vcpu->kvm; - - lockdep_assert_held(&kvm->arch.config_lock); - - if (!kvm_vcpu_has_pmu(vcpu)) - return -ENODEV; - - if (vcpu->arch.pmu.created) - return -EBUSY; - - switch (attr->attr) { - case KVM_ARM_VCPU_PMU_V3_IRQ: { - int __user *uaddr =3D (int __user *)(long)attr->addr; - int irq; - - if (!irqchip_in_kernel(kvm)) - return -EINVAL; - - if (get_user(irq, uaddr)) - return -EFAULT; - - /* The PMU overflow interrupt can be a PPI or a valid SPI. */ - if (!(irq_is_ppi(irq) || irq_is_spi(irq))) - return -EINVAL; - - if (!pmu_irq_is_valid(kvm, irq)) - return -EINVAL; - - if (kvm_arm_pmu_irq_initialized(vcpu)) - return -EBUSY; - - kvm_debug("Set kvm ARM PMU irq: %d\n", irq); - vcpu->arch.pmu.irq_num =3D irq; - return 0; - } - case KVM_ARM_VCPU_PMU_V3_FILTER: { - u8 pmuver =3D kvm_arm_pmu_get_pmuver_limit(); - struct kvm_pmu_event_filter __user *uaddr; - struct kvm_pmu_event_filter filter; - int nr_events; - - /* - * Allow userspace to specify an event filter for the entire - * event range supported by PMUVer of the hardware, rather - * than the guest's PMUVer for KVM backward compatibility. - */ - nr_events =3D __kvm_pmu_event_mask(pmuver) + 1; - - uaddr =3D (struct kvm_pmu_event_filter __user *)(long)attr->addr; - - if (copy_from_user(&filter, uaddr, sizeof(filter))) - return -EFAULT; - - if (((u32)filter.base_event + filter.nevents) > nr_events || - (filter.action !=3D KVM_PMU_EVENT_ALLOW && - filter.action !=3D KVM_PMU_EVENT_DENY)) - return -EINVAL; - - if (kvm_vm_has_ran_once(kvm)) - return -EBUSY; - - if (!kvm->arch.pmu_filter) { - kvm->arch.pmu_filter =3D bitmap_alloc(nr_events, GFP_KERNEL_ACCOUNT); - if (!kvm->arch.pmu_filter) - return -ENOMEM; - - /* - * The default depends on the first applied filter. - * If it allows events, the default is to deny. - * Conversely, if the first filter denies a set of - * events, the default is to allow. - */ - if (filter.action =3D=3D KVM_PMU_EVENT_ALLOW) - bitmap_zero(kvm->arch.pmu_filter, nr_events); - else - bitmap_fill(kvm->arch.pmu_filter, nr_events); - } - - if (filter.action =3D=3D KVM_PMU_EVENT_ALLOW) - bitmap_set(kvm->arch.pmu_filter, filter.base_event, filter.nevents); - else - bitmap_clear(kvm->arch.pmu_filter, filter.base_event, filter.nevents); - - return 0; - } - case KVM_ARM_VCPU_PMU_V3_SET_PMU: { - int __user *uaddr =3D (int __user *)(long)attr->addr; - int pmu_id; - - if (get_user(pmu_id, uaddr)) - return -EFAULT; - - return kvm_arm_pmu_v3_set_pmu(vcpu, pmu_id); - } - case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: { - unsigned int __user *uaddr =3D (unsigned int __user *)(long)attr->addr; - unsigned int n; - - if (get_user(n, uaddr)) - return -EFAULT; - - return kvm_arm_pmu_v3_set_nr_counters(vcpu, n); - } - case KVM_ARM_VCPU_PMU_V3_INIT: - return kvm_arm_pmu_v3_init(vcpu); - } - - return -ENXIO; -} - -int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) -{ - switch (attr->attr) { - case KVM_ARM_VCPU_PMU_V3_IRQ: { - int __user *uaddr =3D (int __user *)(long)attr->addr; - int irq; - - if (!irqchip_in_kernel(vcpu->kvm)) - return -EINVAL; - - if (!kvm_vcpu_has_pmu(vcpu)) - return -ENODEV; - - if (!kvm_arm_pmu_irq_initialized(vcpu)) - return -ENXIO; - - irq =3D vcpu->arch.pmu.irq_num; - return put_user(irq, uaddr); - } - } - - return -ENXIO; -} - -int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) -{ - switch (attr->attr) { - case KVM_ARM_VCPU_PMU_V3_IRQ: - case KVM_ARM_VCPU_PMU_V3_INIT: - case KVM_ARM_VCPU_PMU_V3_FILTER: - case KVM_ARM_VCPU_PMU_V3_SET_PMU: - case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: - if (kvm_vcpu_has_pmu(vcpu)) - return 0; - } - - return -ENXIO; -} - -u8 kvm_arm_pmu_get_pmuver_limit(void) -{ - unsigned int pmuver; - - pmuver =3D SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, - read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1)); - - /* - * Spoof a barebones PMUv3 implementation if the system supports IMPDEF - * traps of the PMUv3 sysregs - */ - if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) - return ID_AA64DFR0_EL1_PMUVer_IMP; - - /* - * Otherwise, treat IMPLEMENTATION DEFINED functionality as - * unimplemented - */ - if (pmuver =3D=3D ID_AA64DFR0_EL1_PMUVer_IMP_DEF) - return 0; - - return min(pmuver, ID_AA64DFR0_EL1_PMUVer_V3P5); -} - -/** - * kvm_vcpu_read_pmcr - Read PMCR_EL0 register for the vCPU - * @vcpu: The vcpu pointer - */ -u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu) -{ - u64 pmcr =3D __vcpu_sys_reg(vcpu, PMCR_EL0); - u64 n =3D vcpu->kvm->arch.nr_pmu_counters; - - if (vcpu_has_nv(vcpu) && !vcpu_is_el2(vcpu)) - n =3D FIELD_GET(MDCR_EL2_HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2)); - - return u64_replace_bits(pmcr, n, ARMV8_PMU_PMCR_N); -} - void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) { bool reprogrammed =3D false; diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index 6b48a3d16d0d5..ee2f0f7e61bcf 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -8,8 +8,22 @@ #include #include =20 +#include + +#include + +static LIST_HEAD(arm_pmus); +static DEFINE_MUTEX(arm_pmus_lock); static DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events); =20 +#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >=3D VGIC_NR= _SGIS) + +bool kvm_supports_guest_pmuv3(void) +{ + guard(mutex)(&arm_pmus_lock); + return !list_empty(&arm_pmus); +} + /* * Given the perf event attributes and system type, determine * if we are going to need to switch counters at guest entry/exit. @@ -209,3 +223,665 @@ void kvm_vcpu_pmu_resync_el0(void) =20 kvm_make_request(KVM_REQ_RESYNC_PMU_EL0, vcpu); } + +void kvm_host_pmu_init(struct arm_pmu *pmu) +{ + struct arm_pmu_entry *entry; + + /* + * Check the sanitised PMU version for the system, as KVM does not + * support implementations where PMUv3 exists on a subset of CPUs. + */ + if (!pmuv3_implemented(kvm_arm_pmu_get_pmuver_limit())) + return; + + guard(mutex)(&arm_pmus_lock); + + entry =3D kmalloc_obj(*entry); + if (!entry) + return; + + entry->arm_pmu =3D pmu; + list_add_tail(&entry->entry, &arm_pmus); +} + +static struct arm_pmu *kvm_pmu_probe_armpmu(void) +{ + struct arm_pmu_entry *entry; + struct arm_pmu *pmu; + int cpu; + + guard(mutex)(&arm_pmus_lock); + + /* + * It is safe to use a stale cpu to iterate the list of PMUs so long as + * the same value is used for the entirety of the loop. Given this, and + * the fact that no percpu data is used for the lookup there is no need + * to disable preemption. + * + * It is still necessary to get a valid cpu, though, to probe for the + * default PMU instance as userspace is not required to specify a PMU + * type. In order to uphold the preexisting behavior KVM selects the + * PMU instance for the core during vcpu init. A dependent use + * case would be a user with disdain of all things big.LITTLE that + * affines the VMM to a particular cluster of cores. + * + * In any case, userspace should just do the sane thing and use the UAPI + * to select a PMU type directly. But, be wary of the baggage being + * carried here. + */ + cpu =3D raw_smp_processor_id(); + list_for_each_entry(entry, &arm_pmus, entry) { + pmu =3D entry->arm_pmu; + + if (cpumask_test_cpu(cpu, &pmu->supported_cpus)) + return pmu; + } + + return NULL; +} + +static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1) +{ + u32 hi[2], lo[2]; + + bitmap_to_arr32(lo, pmu->pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS); + bitmap_to_arr32(hi, pmu->pmceid_ext_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS= ); + + return ((u64)hi[pmceid1] << 32) | lo[pmceid1]; +} + +static u64 compute_pmceid0(struct arm_pmu *pmu) +{ + u64 val =3D __compute_pmceid(pmu, 0); + + /* always support SW_INCR */ + val |=3D BIT(ARMV8_PMUV3_PERFCTR_SW_INCR); + /* always support CHAIN */ + val |=3D BIT(ARMV8_PMUV3_PERFCTR_CHAIN); + return val; +} + +static u64 compute_pmceid1(struct arm_pmu *pmu) +{ + u64 val =3D __compute_pmceid(pmu, 1); + + /* + * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled + * as RAZ + */ + val &=3D ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) | + BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) | + BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32)); + return val; +} + +u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1) +{ + struct arm_pmu *cpu_pmu =3D vcpu->kvm->arch.arm_pmu; + unsigned long *bmap =3D vcpu->kvm->arch.pmu_filter; + u64 val, mask =3D 0; + int base, i, nr_events; + + if (!pmceid1) { + val =3D compute_pmceid0(cpu_pmu); + base =3D 0; + } else { + val =3D compute_pmceid1(cpu_pmu); + base =3D 32; + } + + if (!bmap) + return val; + + nr_events =3D kvm_pmu_event_mask(vcpu->kvm) + 1; + + for (i =3D 0; i < 32; i +=3D 8) { + u64 byte; + + byte =3D bitmap_get_value8(bmap, base + i); + mask |=3D byte << i; + if (nr_events >=3D (0x4000 + base + 32)) { + byte =3D bitmap_get_value8(bmap, 0x4000 + base + i); + mask |=3D byte << (32 + i); + } + } + + return val & mask; +} + +/* + * When perf interrupt is an NMI, we cannot safely notify the vcpu corresp= onding + * to the event. + * This is why we need a callback to do it once outside of the NMI context. + */ +static void kvm_pmu_perf_overflow_notify_vcpu(struct irq_work *work) +{ + struct kvm_vcpu *vcpu; + + vcpu =3D container_of(work, struct kvm_vcpu, arch.pmu.overflow_work); + kvm_vcpu_kick(vcpu); +} + +static u32 __kvm_pmu_event_mask(unsigned int pmuver) +{ + switch (pmuver) { + case ID_AA64DFR0_EL1_PMUVer_IMP: + return GENMASK(9, 0); + case ID_AA64DFR0_EL1_PMUVer_V3P1: + case ID_AA64DFR0_EL1_PMUVer_V3P4: + case ID_AA64DFR0_EL1_PMUVer_V3P5: + case ID_AA64DFR0_EL1_PMUVer_V3P7: + return GENMASK(15, 0); + default: /* Shouldn't be here, just for sanity */ + WARN_ONCE(1, "Unknown PMU version %d\n", pmuver); + return 0; + } +} + +u32 kvm_pmu_event_mask(struct kvm *kvm) +{ + u64 dfr0 =3D kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1); + u8 pmuver =3D SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0); + + return __kvm_pmu_event_mask(pmuver); +} + +u64 kvm_pmu_evtyper_mask(struct kvm *kvm) +{ + u64 mask =3D ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 | + kvm_pmu_event_mask(kvm); + + if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL2, IMP)) + mask |=3D ARMV8_PMU_INCLUDE_EL2; + + if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP)) + mask |=3D ARMV8_PMU_EXCLUDE_NS_EL0 | + ARMV8_PMU_EXCLUDE_NS_EL1 | + ARMV8_PMU_EXCLUDE_EL3; + + return mask; +} + +static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) +{ + struct kvm_pmu *pmu =3D &vcpu->arch.pmu; + bool overflow; + + overflow =3D kvm_pmu_overflow_status(vcpu); + if (pmu->irq_level =3D=3D overflow) + return; + + pmu->irq_level =3D overflow; + + if (likely(irqchip_in_kernel(vcpu->kvm))) { + int ret =3D kvm_vgic_inject_irq(vcpu->kvm, vcpu, + pmu->irq_num, overflow, pmu); + WARN_ON(ret); + } +} + +/** + * kvm_pmu_flush_hwstate - flush pmu state to cpu + * @vcpu: The vcpu pointer + * + * Check if the PMU has overflowed while we were running in the host, and = inject + * an interrupt if that was the case. + */ +void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu) +{ + kvm_pmu_update_state(vcpu); +} + +/** + * kvm_pmu_sync_hwstate - sync pmu state from cpu + * @vcpu: The vcpu pointer + * + * Check if the PMU has overflowed while we were running in the guest, and + * inject an interrupt if that was the case. + */ +void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu) +{ + kvm_pmu_update_state(vcpu); +} + +int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu) +{ + if (!vcpu->arch.pmu.created) + return -EINVAL; + + /* + * A valid interrupt configuration for the PMU is either to have a + * properly configured interrupt number and using an in-kernel + * irqchip, or to not have an in-kernel GIC and not set an IRQ. + */ + if (irqchip_in_kernel(vcpu->kvm)) { + int irq =3D vcpu->arch.pmu.irq_num; + /* + * If we are using an in-kernel vgic, at this point we know + * the vgic will be initialized, so we can check the PMU irq + * number against the dimensions of the vgic and make sure + * it's valid. + */ + if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq)) + return -EINVAL; + } else if (kvm_arm_pmu_irq_initialized(vcpu)) { + return -EINVAL; + } + + return 0; +} + +static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu) +{ + if (irqchip_in_kernel(vcpu->kvm)) { + int ret; + + /* + * If using the PMU with an in-kernel virtual GIC + * implementation, we require the GIC to be already + * initialized when initializing the PMU. + */ + if (!vgic_initialized(vcpu->kvm)) + return -ENODEV; + + if (!kvm_arm_pmu_irq_initialized(vcpu)) + return -ENXIO; + + ret =3D kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num, + &vcpu->arch.pmu); + if (ret) + return ret; + } + + init_irq_work(&vcpu->arch.pmu.overflow_work, + kvm_pmu_perf_overflow_notify_vcpu); + + vcpu->arch.pmu.created =3D true; + return 0; +} + +/* + * For one VM the interrupt type must be same for each vcpu. + * As a PPI, the interrupt number is the same for all vcpus, + * while as an SPI it must be a separate number per vcpu. + */ +static bool pmu_irq_is_valid(struct kvm *kvm, int irq) +{ + unsigned long i; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!kvm_arm_pmu_irq_initialized(vcpu)) + continue; + + if (irq_is_ppi(irq)) { + if (vcpu->arch.pmu.irq_num !=3D irq) + return false; + } else { + if (vcpu->arch.pmu.irq_num =3D=3D irq) + return false; + } + } + + return true; +} + +/** + * kvm_arm_pmu_get_max_counters - Return the max number of PMU counters. + * @kvm: The kvm pointer + */ +u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm) +{ + struct arm_pmu *arm_pmu =3D kvm->arch.arm_pmu; + + /* + * PMUv3 requires that all event counters are capable of counting any + * event, though the same may not be true of non-PMUv3 hardware. + */ + if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) + return 1; + + /* + * The arm_pmu->cntr_mask considers the fixed counter(s) as well. + * Ignore those and return only the general-purpose counters. + */ + return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS); +} + +static void kvm_arm_set_nr_counters(struct kvm *kvm, unsigned int nr) +{ + kvm->arch.nr_pmu_counters =3D nr; + + /* Reset MDCR_EL2.HPMN behind the vcpus' back... */ + if (test_bit(KVM_ARM_VCPU_HAS_EL2, kvm->arch.vcpu_features)) { + struct kvm_vcpu *vcpu; + unsigned long i; + + kvm_for_each_vcpu(i, vcpu, kvm) { + u64 val =3D __vcpu_sys_reg(vcpu, MDCR_EL2); + + val &=3D ~MDCR_EL2_HPMN; + val |=3D FIELD_PREP(MDCR_EL2_HPMN, kvm->arch.nr_pmu_counters); + __vcpu_assign_sys_reg(vcpu, MDCR_EL2, val); + } + } +} + +static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu) +{ + lockdep_assert_held(&kvm->arch.config_lock); + + kvm->arch.arm_pmu =3D arm_pmu; + kvm_arm_set_nr_counters(kvm, kvm_arm_pmu_get_max_counters(kvm)); +} + +/** + * kvm_arm_set_default_pmu - No PMU set, get the default one. + * @kvm: The kvm pointer + * + * The observant among you will notice that the supported_cpus + * mask does not get updated for the default PMU even though it + * is quite possible the selected instance supports only a + * subset of cores in the system. This is intentional, and + * upholds the preexisting behavior on heterogeneous systems + * where vCPUs can be scheduled on any core but the guest + * counters could stop working. + */ +int kvm_arm_set_default_pmu(struct kvm *kvm) +{ + struct arm_pmu *arm_pmu =3D kvm_pmu_probe_armpmu(); + + if (!arm_pmu) + return -ENODEV; + + kvm_arm_set_pmu(kvm, arm_pmu); + return 0; +} + +static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id) +{ + struct kvm *kvm =3D vcpu->kvm; + struct arm_pmu_entry *entry; + struct arm_pmu *arm_pmu; + int ret =3D -ENXIO; + + lockdep_assert_held(&kvm->arch.config_lock); + mutex_lock(&arm_pmus_lock); + + list_for_each_entry(entry, &arm_pmus, entry) { + arm_pmu =3D entry->arm_pmu; + if (arm_pmu->pmu.type =3D=3D pmu_id) { + if (kvm_vm_has_ran_once(kvm) || + (kvm->arch.pmu_filter && kvm->arch.arm_pmu !=3D arm_pmu)) { + ret =3D -EBUSY; + break; + } + + kvm_arm_set_pmu(kvm, arm_pmu); + cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus); + ret =3D 0; + break; + } + } + + mutex_unlock(&arm_pmus_lock); + return ret; +} + +static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned = int n) +{ + struct kvm *kvm =3D vcpu->kvm; + + if (!kvm->arch.arm_pmu) + return -EINVAL; + + if (n > kvm_arm_pmu_get_max_counters(kvm)) + return -EINVAL; + + kvm_arm_set_nr_counters(kvm, n); + return 0; +} + +int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) +{ + struct kvm *kvm =3D vcpu->kvm; + + lockdep_assert_held(&kvm->arch.config_lock); + + if (!kvm_vcpu_has_pmu(vcpu)) + return -ENODEV; + + if (vcpu->arch.pmu.created) + return -EBUSY; + + switch (attr->attr) { + case KVM_ARM_VCPU_PMU_V3_IRQ: { + int __user *uaddr =3D (int __user *)(long)attr->addr; + int irq; + + if (!irqchip_in_kernel(kvm)) + return -EINVAL; + + if (get_user(irq, uaddr)) + return -EFAULT; + + /* The PMU overflow interrupt can be a PPI or a valid SPI. */ + if (!(irq_is_ppi(irq) || irq_is_spi(irq))) + return -EINVAL; + + if (!pmu_irq_is_valid(kvm, irq)) + return -EINVAL; + + if (kvm_arm_pmu_irq_initialized(vcpu)) + return -EBUSY; + + kvm_debug("Set kvm ARM PMU irq: %d\n", irq); + vcpu->arch.pmu.irq_num =3D irq; + return 0; + } + case KVM_ARM_VCPU_PMU_V3_FILTER: { + u8 pmuver =3D kvm_arm_pmu_get_pmuver_limit(); + struct kvm_pmu_event_filter __user *uaddr; + struct kvm_pmu_event_filter filter; + int nr_events; + + /* + * Allow userspace to specify an event filter for the entire + * event range supported by PMUVer of the hardware, rather + * than the guest's PMUVer for KVM backward compatibility. + */ + nr_events =3D __kvm_pmu_event_mask(pmuver) + 1; + + uaddr =3D (struct kvm_pmu_event_filter __user *)(long)attr->addr; + + if (copy_from_user(&filter, uaddr, sizeof(filter))) + return -EFAULT; + + if (((u32)filter.base_event + filter.nevents) > nr_events || + (filter.action !=3D KVM_PMU_EVENT_ALLOW && + filter.action !=3D KVM_PMU_EVENT_DENY)) + return -EINVAL; + + if (kvm_vm_has_ran_once(kvm)) + return -EBUSY; + + if (!kvm->arch.pmu_filter) { + kvm->arch.pmu_filter =3D bitmap_alloc(nr_events, GFP_KERNEL_ACCOUNT); + if (!kvm->arch.pmu_filter) + return -ENOMEM; + + /* + * The default depends on the first applied filter. + * If it allows events, the default is to deny. + * Conversely, if the first filter denies a set of + * events, the default is to allow. + */ + if (filter.action =3D=3D KVM_PMU_EVENT_ALLOW) + bitmap_zero(kvm->arch.pmu_filter, nr_events); + else + bitmap_fill(kvm->arch.pmu_filter, nr_events); + } + + if (filter.action =3D=3D KVM_PMU_EVENT_ALLOW) + bitmap_set(kvm->arch.pmu_filter, filter.base_event, filter.nevents); + else + bitmap_clear(kvm->arch.pmu_filter, filter.base_event, filter.nevents); + + return 0; + } + case KVM_ARM_VCPU_PMU_V3_SET_PMU: { + int __user *uaddr =3D (int __user *)(long)attr->addr; + int pmu_id; + + if (get_user(pmu_id, uaddr)) + return -EFAULT; + + return kvm_arm_pmu_v3_set_pmu(vcpu, pmu_id); + } + case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: { + unsigned int __user *uaddr =3D (unsigned int __user *)(long)attr->addr; + unsigned int n; + + if (get_user(n, uaddr)) + return -EFAULT; + + return kvm_arm_pmu_v3_set_nr_counters(vcpu, n); + } + case KVM_ARM_VCPU_PMU_V3_INIT: + return kvm_arm_pmu_v3_init(vcpu); + } + + return -ENXIO; +} + +int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) +{ + switch (attr->attr) { + case KVM_ARM_VCPU_PMU_V3_IRQ: { + int __user *uaddr =3D (int __user *)(long)attr->addr; + int irq; + + if (!irqchip_in_kernel(vcpu->kvm)) + return -EINVAL; + + if (!kvm_vcpu_has_pmu(vcpu)) + return -ENODEV; + + if (!kvm_arm_pmu_irq_initialized(vcpu)) + return -ENXIO; + + irq =3D vcpu->arch.pmu.irq_num; + return put_user(irq, uaddr); + } + } + + return -ENXIO; +} + +int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr = *attr) +{ + switch (attr->attr) { + case KVM_ARM_VCPU_PMU_V3_IRQ: + case KVM_ARM_VCPU_PMU_V3_INIT: + case KVM_ARM_VCPU_PMU_V3_FILTER: + case KVM_ARM_VCPU_PMU_V3_SET_PMU: + case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: + if (kvm_vcpu_has_pmu(vcpu)) + return 0; + } + + return -ENXIO; +} + +u8 kvm_arm_pmu_get_pmuver_limit(void) +{ + unsigned int pmuver; + + pmuver =3D SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, + read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1)); + + /* + * Spoof a barebones PMUv3 implementation if the system supports IMPDEF + * traps of the PMUv3 sysregs + */ + if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) + return ID_AA64DFR0_EL1_PMUVer_IMP; + + /* + * Otherwise, treat IMPLEMENTATION DEFINED functionality as + * unimplemented + */ + if (pmuver =3D=3D ID_AA64DFR0_EL1_PMUVer_IMP_DEF) + return 0; + + return min(pmuver, ID_AA64DFR0_EL1_PMUVer_V3P5); +} + +u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu) +{ + u64 val =3D FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu)); + + if (val =3D=3D 0) + return BIT(ARMV8_PMU_CYCLE_IDX); + else + return GENMASK(val - 1, 0) | BIT(ARMV8_PMU_CYCLE_IDX); +} + +u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu) +{ + unsigned int hpmn, n; + + if (!vcpu_has_nv(vcpu)) + return 0; + + hpmn =3D SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2)); + n =3D vcpu->kvm->arch.nr_pmu_counters; + + /* + * Programming HPMN to a value greater than PMCR_EL0.N is + * CONSTRAINED UNPREDICTABLE. Make the implementation choice that an + * UNKNOWN number of counters (in our case, zero) are reserved for EL2. + */ + if (hpmn >=3D n) + return 0; + + /* + * Programming HPMN=3D0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't + * implemented. Since KVM's ability to emulate HPMN=3D0 does not directly + * depend on hardware (all PMU registers are trapped), make the + * implementation choice that all counters are included in the second + * range reserved for EL2/EL3. + */ + return GENMASK(n - 1, hpmn); +} + +bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int idx) +{ + return kvm_pmu_hyp_counter_mask(vcpu) & BIT(idx); +} + +u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu) +{ + u64 mask =3D kvm_pmu_implemented_counter_mask(vcpu); + + if (!vcpu_has_nv(vcpu) || vcpu_is_el2(vcpu)) + return mask; + + return mask & ~kvm_pmu_hyp_counter_mask(vcpu); +} + +/** + * kvm_vcpu_read_pmcr - Read PMCR_EL0 register for the vCPU + * @vcpu: The vcpu pointer + */ +u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu) +{ + u64 pmcr =3D __vcpu_sys_reg(vcpu, PMCR_EL0); + u64 n =3D vcpu->kvm->arch.nr_pmu_counters; + + if (vcpu_has_nv(vcpu) && !vcpu_is_el2(vcpu)) + n =3D FIELD_GET(MDCR_EL2_HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2)); + + return u64_replace_bits(pmcr, n, ARMV8_PMU_PMCR_N); +} diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index e91d15a7a564b..24a471cf59d56 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -53,13 +53,16 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u6= 4 select_idx); void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 = val); void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx,= u64 val); u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu); +u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu); u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu); +u32 kvm_pmu_event_mask(struct kvm *kvm); u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1); void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu); void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu); void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vcpu, u64 val); void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu); +bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu); bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu); void kvm_pmu_update_run(struct kvm_vcpu *vcpu); void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val); @@ -132,6 +135,10 @@ static inline u64 kvm_pmu_accessible_counter_mask(stru= ct kvm_vcpu *vcpu) { return 0; } +static inline u32 kvm_pmu_event_mask(struct kvm *kvm) +{ + return 0; +} static inline void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu) {} static inline void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu) {} static inline void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vcpu, u= 64 val) {} --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oi1-f202.google.com (mail-oi1-f202.google.com [209.85.167.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 22F823DDDA1 for ; Mon, 4 May 2026 21:18:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; cv=none; b=fshgYnVhPb8lZ9NlSkXjzNfi3KP3/7QTJuRfBypcDgjwxsBNhKv1SzVxD9L492YA/VwRZpqI8cTAJijJT6XWLvlSFiIk/6FjCvRI89r5tMM/9DkjS884nmQpvh8lMP8Suvw3gIhw8VuWaQmFUpbsqALV8My5WpgkJ6fq92yYMsg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; c=relaxed/simple; bh=ueTqc2Ph7z17HXoEzMvMnAeIJNA23UDUjdS2CztRW2k=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=UeuJP7evVJ/bqHEZ3FKdZuvOpTwqhd9Yhk7AaiHbb9tRMeQQ3uBzO5c4CmzRoeT1AHE7euJqM6+5uHLVsxABgZpbovhTyNmNHXKLNDZNPYuWj6GAra/9wRQXaKmVkP0PpVxANmd9QjNd8Xdlvgm/yx16zVJtl3CcyRDL4xTwMEg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=kDpKq3VW; arc=none smtp.client-ip=209.85.167.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="kDpKq3VW" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-4718a1723a5so6795671b6e.3 for ; Mon, 04 May 2026 14:18:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929513; x=1778534313; darn=vger.kernel.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=thCWuABQmlCLQCDjA65mDdmemz/sfk/Mfmpa4HEmTAo=; b=kDpKq3VWqY0kZ2SG8Coyapb//KD+KV5eLXxLz8i7Q3UMzdMcieiuc63hQ3FW399M2C rFDG9uw1fLn4O6pUSF9hxb1E+kYgYtli5lHuBwJRpuFsK7GMtlb3wteetiqAQQFPBkce WHs86taFK3SD6PmDd+WZIvesxEmBc1ec24egzpEqkjvlgzM5oOhpTkAgb5UepzYCw53I wy5pyxiZ9jZRQhdNShWI29UwsSivEABZ2HS4GjWbbjrUygWkT3zdAwZssv6kLID10ZoG oleVQQYS0qA63MXy53F8D37tdX15+F6W3/fpfAIlSWia/7f59wMlYzxZDZeKBS5WzjvU bJIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929513; x=1778534313; 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=thCWuABQmlCLQCDjA65mDdmemz/sfk/Mfmpa4HEmTAo=; b=OIa/wE8Mhc9GMo6wVkA7vx6A31lQPS2j2vecVTVEwDRjNxZ9sjatBeQJhqnsUYXlbs 5ZH3TQoy5gGagEqx7inb3HlgvoIp+bi8AApKSpw0hBTKJglCOD7Ioo5RrnI7MmbG+PUs b6fKR0Sknd9s5rdGhPiHnR1B/RH4QA53MYSLR0EiernY7P+sZfVD9gw73TZuRoQvV9Rj Ow/mK9c6+ZggwLImHAEKkDhPx3OIfV93o6T71vCZ2ZLQ8Ieqa1velgnSiolwtkG52A7t e6XW8229iU8gzDX/lBBZCu3ofhaoXcAYTrV5tkltVSvZqwkmDfD7OY16okDCtvGO5+Y0 RPQQ== X-Forwarded-Encrypted: i=1; AFNElJ/IXLvaPXdsXw+/82Aq1GBncj6av9s92Ll3jozFAjKtN0uzUpnK5XDWrl0lae3ixz+vCfdWdQJ6dcy0CeM=@vger.kernel.org X-Gm-Message-State: AOJu0YwcAFoaWCSTFxIirRIkHq8R+YdKAABUJkwSprJ7eAk+vUqHrHG+ RT6yP9sT6w3k/4XdaE2hbZH+jk8qhDLvDGugT425RH588KVbfnvkwisK7RyXR79Q120/qY0LFoJ D06SMwSayxI5ApHmv37vuNC/Q5Q== X-Received: from ilst10.prod.google.com ([2002:a05:6e02:60a:b0:4fc:39c6:a601]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:f08:b0:696:32ad:e894 with SMTP id 006d021491bc7-696979ec5c7mr5763143eaf.23.1777929512987; Mon, 04 May 2026 14:18:32 -0700 (PDT) Date: Mon, 4 May 2026 21:17:57 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-5-coltonlewis@google.com> Subject: [PATCH v7 04/20] perf: arm_pmuv3: Generalize counter bitmasks From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The OVSR bitmasks are valid for enable and interrupt registers as well as overflow registers. Generalize the names. Acked-by: Mark Rutland Signed-off-by: Colton Lewis --- drivers/perf/arm_pmuv3.c | 4 ++-- include/linux/perf/arm_pmuv3.h | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 8d3b832cd633a..1cceb1f614515 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -534,7 +534,7 @@ static void armv8pmu_pmcr_write(u64 val) =20 static int armv8pmu_has_overflowed(u64 pmovsr) { - return !!(pmovsr & ARMV8_PMU_OVERFLOWED_MASK); + return !!(pmovsr & ARMV8_PMU_CNT_MASK_ALL); } =20 static int armv8pmu_counter_has_overflowed(u64 pmnc, int idx) @@ -770,7 +770,7 @@ static u64 armv8pmu_getreset_flags(void) value =3D read_pmovsclr(); =20 /* Write to clear flags */ - value &=3D ARMV8_PMU_OVERFLOWED_MASK; + value &=3D ARMV8_PMU_CNT_MASK_ALL; write_pmovsclr(value); =20 return value; diff --git a/include/linux/perf/arm_pmuv3.h b/include/linux/perf/arm_pmuv3.h index d698efba28a27..fd2a34b4a64d1 100644 --- a/include/linux/perf/arm_pmuv3.h +++ b/include/linux/perf/arm_pmuv3.h @@ -224,14 +224,14 @@ ARMV8_PMU_PMCR_LC | ARMV8_PMU_PMCR_LP) =20 /* - * PMOVSR: counters overflow flag status reg + * Counter bitmask layouts for overflow, enable, and interrupts */ -#define ARMV8_PMU_OVSR_P GENMASK(30, 0) -#define ARMV8_PMU_OVSR_C BIT(31) -#define ARMV8_PMU_OVSR_F BIT_ULL(32) /* arm64 only */ -/* Mask for writable bits is both P and C fields */ -#define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C | \ - ARMV8_PMU_OVSR_F) +#define ARMV8_PMU_CNT_MASK_P GENMASK(30, 0) +#define ARMV8_PMU_CNT_MASK_C BIT(31) +#define ARMV8_PMU_CNT_MASK_F BIT_ULL(32) /* arm64 only */ +#define ARMV8_PMU_CNT_MASK_ALL (ARMV8_PMU_CNT_MASK_P | \ + ARMV8_PMU_CNT_MASK_C | \ + ARMV8_PMU_CNT_MASK_F) =20 /* * PMXEVTYPER: Event selection reg --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f74.google.com (mail-ot1-f74.google.com [209.85.210.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 967A73EF64D for ; Mon, 4 May 2026 21:18:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; cv=none; b=B5Z7v68CPp8af+eKfqKuY/AzPilVzMR6F4x6N4LUaCPyG8rX+77xYE5yJNUxpAimeThWzaykhtdbsmEbygdGU1hqz0GQn8JweWfZ27cOor7LnO+NqwGL/96CAqxXMMcbM5ODqbA8SnunpXVpc/YPnlb3wQiUt4qWRuHEg6HIO94= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929518; c=relaxed/simple; bh=Kt0JxjDXw64yyFAEbn3FTtOy6aQbbH4PD8HMmdQ27Kc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QYavhy0JJm0pvuMQkMuSf82aGLnf2VcUIQpmTEx+ZKIvEuxBvWwu9Eh0+uUjEP/L0haA3YD9C6fdCV+PBHb5WGyv4Zyw/UdGVGkeN9YX/bMWG7y1BUnXbZ5586N6U/kTQZPQf2yh2p17VJ7iJLpHkmR4lXKk6namCGpTlQZxQXQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=HlhMW9VE; arc=none smtp.client-ip=209.85.210.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="HlhMW9VE" Received: by mail-ot1-f74.google.com with SMTP id 46e09a7af769-7dbd4fe0e15so10350969a34.3 for ; Mon, 04 May 2026 14:18:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929514; x=1778534314; darn=vger.kernel.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=zpZCDOiFD5ADpp1wKWAJRO360JAEeKFaw0Z2nRZPnRE=; b=HlhMW9VE0NFY70PhISGn17Oofl80Z56pJcbBKpIluqSVljhaZ8buUUJO9fzngWsVdL Fy+ZafFrc0cZFRXAvUMSfROgONzy9Nnd6OZmVBN4PIAw6cUdb3yPkcjc364kUKimMp7M RWp0ahV1XdNXjbmIU1+DALrzRCBW3eiMpfsjzd1gNNZmcWCyQyu5Iu0AXnAOnf4V5W2h Vnz3U95GVyzS0bSiEL+zNk71PrescclXwHhvqe5PRchMLvr5mFxPcvhcplqcSFF9e9y7 iGjcPAW+flEX1gE79anaXR2MLqwoSOvp86M8He78uA13Aw31nspLJOoYx4mjNMnfQnkH 3/ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929514; x=1778534314; 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=zpZCDOiFD5ADpp1wKWAJRO360JAEeKFaw0Z2nRZPnRE=; b=lBY3vbMkdbby47IejEj7zEm63xPDwzPrRUucNJvlYgg9EF5S4r6fJnVWH3gZtURuz+ N+0HC3VidbnU3MChBiugSAx+NsDPQWBb0wmjgkWv72zPDLLQKRljlU5XRDw2l0pzcgVE zg01DeSHz/kxklm4UnZuWz1dWz8zt/mK7bL/3PPUfpYCfMPqWoIKUnhkoLGDS7UFi+Vl Ijr+5UX/L94d+Or/Cg/xE96+9gvZKI7k6rUu6xX2hJvUAJI3lyufphjDztCTf0FQIOCa ehBy4wEzrW1yyKZ1sJxRtPBORUkVzSS39HWXLZ5qvxr/OQ4QyNhhW7UD57rXIdTJAgFk cWnQ== X-Forwarded-Encrypted: i=1; AFNElJ8Z4fq9aRAsUHB72TRPZ6qQW1KYzQaPI2DCWJWEU/b38QRntbV11Smc2yHUdIndTQi+FXJCLWiODVU13D4=@vger.kernel.org X-Gm-Message-State: AOJu0YxJIsnvlVtaMS8OdByPvwB+cJj96qsHrxXFD4ZdazjHUIkO1AE0 iNEU/et1KBNa0ITvyqoRdlE9/hQPyAeGo5GuufuKR/sKB5gzl8KUCBueReFXN9moP1XYVSNwdaX q1pvJJ7KPFK/vberrsgoqDtCZ6w== X-Received: from ilqa6.prod.google.com ([2002:a05:6e02:1206:b0:4fa:1fcf:cad3]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:99b:b0:694:8bfa:7817 with SMTP id 006d021491bc7-69697db8330mr6011884eaf.53.1777929514318; Mon, 04 May 2026 14:18:34 -0700 (PDT) Date: Mon, 4 May 2026 21:17:58 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-6-coltonlewis@google.com> Subject: [PATCH v7 05/20] perf: arm_pmuv3: Check cntr_mask before using pmccntr From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Check cntr_mask before using pmccntr to ensure it's available. With a partitioned PMU, there may be instances where pmccntr is being used by the guest and will ba absent from cntr_mask. Signed-off-by: Colton Lewis --- drivers/perf/arm_pmuv3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 1cceb1f614515..7ff3139dda893 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -993,6 +993,9 @@ static bool armv8pmu_can_use_pmccntr(struct pmu_hw_even= ts *cpuc, if (evtype !=3D ARMV8_PMUV3_PERFCTR_CPU_CYCLES) return false; =20 + if (!test_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask)) + return false; + /* * A CPU_CYCLES event with threshold counting cannot use PMCCNTR_EL0 * since it lacks threshold support. --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D89AF3FE66C for ; Mon, 4 May 2026 21:18:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929519; cv=none; b=VHFn4NtkQg37eW4IjBQQsYFH97wztVYC1N5GnYxIQwR19DBMiQ9uAKzDv15yHGoXpqImToz8BQV9YEEDWkrdqFNlQKeYfLhylJv/P7xtNLgrsICU83GOLJSaKVdc1hYYgCSOScDUHwf0sdteoXIVisFL2fvLP/GsMJc0x47WBMk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929519; c=relaxed/simple; bh=PCwE1U1S8GJICe1ZzrEReuZXJiFR/hMgArdNomzfnhs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Pcve+4UVLdP5+k2OaCf8S0auWU1Zpd7JENQ1iOL5bCS+eKRt20RLatQOATJ1H8DoJqzkJshcXXB3eFFk8m5zVKEbuwQKcLtq+zEvJUJYlcm/6iSUit+yDkUIRLo7/+0bejh1NpH39qieIqHkZAFSbrxY4FR0Zri2HaKBZaXzKc0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=pjHqI3Na; arc=none smtp.client-ip=209.85.161.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pjHqI3Na" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-694867ca29cso9398448eaf.2 for ; Mon, 04 May 2026 14:18:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929516; x=1778534316; darn=vger.kernel.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=+6bOFPp2SwRqs2Ha+Wen4rE5RTS/9bf2wEDavZhr5Uo=; b=pjHqI3NaS8+5GOC3vXpZ67uVK0u4KEk599JziTFS4+OatSYh9rYTWD2582+R0XIhM3 qGi/C60UeBYF/ZWVZP3yvDO7lyu5MWM0xNAeACjgw0edwqtCVapSD6ZU1/J+XP0sJaSo dBdZC3iy2R0wsDyuOft9MG/nZTWwDYvPkEj1XGkzBqPm/tF0WR4h4ggB5WXDlUmsbiBt XcbGFqIHi6mWr9dvqSqOqovIJjqGElmOk3SlYu+3s5nqJZqE5rSiRhAgolTQB2kU7oHN kKiIeQbI176hQ6gg02Sg0bFoI5SuSDohPiWzWRZ99Kdkt6N3ly8WJqlzckRaHQIT5duU yq2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929516; x=1778534316; 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=+6bOFPp2SwRqs2Ha+Wen4rE5RTS/9bf2wEDavZhr5Uo=; b=ri2lvTiGnBdBIS/qhTkMP54za16AAIZPIRJ2vP2UMUJtd7pQgAwijolw+5hGFnP2Kc LYVNL68V7c/Y477hYgqtlvCZGxJUZuSaLSR47OAPCq6dPV8fZoyyV1PYUL6LIAaEzNct Kss2yAV8UmA3XrZLtH/h9lpPWycZHNMeOUtzbq5GjgiQtUtOEXEBEkCPjORXvbOMtUpl T4lokFZLxlbQOVL/1uWdIWQpSAOkK6ctpjrEuF28GAIgXSaQVwYHKmCpyLi0SNp9irG+ WcNjGq35v32F9nQetrahDVzzIa5ouc6EqCv1LLCaMhSiiB8S/AXXXfW6P3BCoMaMzrH7 f+9w== X-Forwarded-Encrypted: i=1; AFNElJ/H//sa85QmZpGcFvxk9ANwHxVQofmAgiGS4A79/Vp15DUdr08gSj6sQXCQLll3JD3ksxwurTTpqjmgcEw=@vger.kernel.org X-Gm-Message-State: AOJu0YyPF4L8XiaXR3kMIJRnCvw0PuyCgBMlOScFPDCnH21nAlSEebDW rQZYzHR/zMDOZM1TycBITfQQQ7fUamLf1RWG5unG11B05H6W5oh20xuGqOg9hbhpEiPXsiTAOLv wEHkho8IKjUxF8JXy7+zVnb/SkQ== X-Received: from ioel2.prod.google.com ([2002:a05:6602:2762:b0:963:c1c8:da0e]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:827:b0:696:155e:cd67 with SMTP id 006d021491bc7-6998d47028bmr144148eaf.57.1777929515654; Mon, 04 May 2026 14:18:35 -0700 (PDT) Date: Mon, 4 May 2026 21:17:59 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-7-coltonlewis@google.com> Subject: [PATCH v7 06/20] perf: arm_pmuv3: Add method to partition the PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 a module parameter reserved_host_counters to reserve a number of counters for the host. Counters not reserved for the host may be used by a guest VM when the PMU is partitioned. Add the function armv8pmu_partition() to check the validity of the reservation and record a partition has happened and the maximum allowable value for HPMN. Due to the difficulty this feature would create for the driver running in nVHE mode, partitioning is only allowed in VHE mode. In order to support a partitioning on nVHE we'd need to explicitly disable guest counters on every exit and reset HPMN to place all counters in the first range. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 4 ++ arch/arm64/include/asm/arm_pmuv3.h | 5 ++ arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/pmu-direct.c | 22 +++++++++ drivers/perf/arm_pmuv3.c | 77 ++++++++++++++++++++++++++++-- include/kvm/arm_pmu.h | 8 ++++ include/linux/perf/arm_pmu.h | 2 + 7 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 arch/arm64/kvm/pmu-direct.c diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index 2ec0e5e83fc98..154503f054886 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 per= f_event_attr *attr) return false; } =20 +static inline bool has_host_pmu_partition_support(void) +{ + return false; +} static inline bool kvm_set_pmuserenr(u64 val) { return false; diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/ar= m_pmuv3.h index cf2b2212e00a2..27c4d6d47da31 100644 --- a/arch/arm64/include/asm/arm_pmuv3.h +++ b/arch/arm64/include/asm/arm_pmuv3.h @@ -171,6 +171,11 @@ static inline bool pmuv3_implemented(int pmuver) pmuver =3D=3D ID_AA64DFR0_EL1_PMUVer_NI); } =20 +static inline bool is_pmuv3p1(int pmuver) +{ + return pmuver >=3D ID_AA64DFR0_EL1_PMUVer_V3P1; +} + static inline bool is_pmuv3p4(int pmuver) { return pmuver >=3D ID_AA64DFR0_EL1_PMUVer_V3P4; diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 3ebc0570345cc..baf0f296c0e53 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -26,7 +26,7 @@ kvm-y +=3D arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.= o \ vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o \ vgic/vgic-v5.o =20 -kvm-$(CONFIG_HW_PERF_EVENTS) +=3D pmu-emul.o pmu.o +kvm-$(CONFIG_HW_PERF_EVENTS) +=3D pmu-emul.o pmu-direct.o pmu.o kvm-$(CONFIG_ARM64_PTR_AUTH) +=3D pauth.o kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) +=3D ptdump.o =20 diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c new file mode 100644 index 0000000000000..74e40e4915416 --- /dev/null +++ b/arch/arm64/kvm/pmu-direct.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Google LLC + * Author: Colton Lewis + */ + +#include + +#include + +/** + * has_host_pmu_partition_support() - Determine if partitioning is possible + * + * Partitioning is only supported in VHE mode with PMUv3 + * + * Return: True if partitioning is possible, false otherwise + */ +bool has_host_pmu_partition_support(void) +{ + return has_vhe() && + system_supports_pmuv3(); +} diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 7ff3139dda893..6e447227d801f 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -42,6 +42,13 @@ #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_ACCESS 0xEC #define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_MISS 0xED =20 +static int reserved_host_counters __read_mostly =3D -1; +bool armv8pmu_is_partitioned; + +module_param(reserved_host_counters, int, 0); +MODULE_PARM_DESC(reserved_host_counters, + "PMU Partition: -1 =3D No partition; +N =3D Reserve N counters for the = host"); + /* * ARMv8 Architectural defined events, not all of these may * be supported on any given implementation. Unsupported events will @@ -532,6 +539,11 @@ static void armv8pmu_pmcr_write(u64 val) write_pmcr(val); } =20 +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_CNT_MASK_ALL); @@ -1312,6 +1324,54 @@ struct armv8pmu_probe_info { bool present; }; =20 +/** + * 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 >=3D 0 && + host_counters <=3D 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 max_guest_counters 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)) { + pr_err("PMU partition reservation of %d host counters is not valid", hos= t_counters); + return -EINVAL; + } + + nr_counters =3D armv8pmu_pmcr_n_read(); + hpmn =3D nr_counters - host_counters; + + pmu->max_guest_counters =3D hpmn; + armv8pmu_is_partitioned =3D true; + + pr_info("Partitioned PMU with %d host counters -> %u guest counters", hos= t_counters, hpmn); + + return 0; +} + static void __armv8pmu_probe_pmu(void *info) { struct armv8pmu_probe_info *probe =3D info; @@ -1326,17 +1386,26 @@ static void __armv8pmu_probe_pmu(void *info) =20 cpu_pmu->pmuver =3D pmuver; probe->present =3D true; + cpu_pmu->max_guest_counters =3D -1; =20 /* 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->hw_cntr_mask, 0, armv8pmu_pmcr_n_read()); =20 /* Add the CPU cycles counter */ - set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask); + set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->hw_cntr_mask); =20 /* Add the CPU instructions counter */ if (pmuv3_has_icntr()) - set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask); + set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->hw_cntr_mask); + + bitmap_copy(cpu_pmu->cntr_mask, cpu_pmu->hw_cntr_mask, ARMPMU_MAX_HWEVENT= S); + + if (reserved_host_counters >=3D 0) { + if (has_host_pmu_partition_support()) + armv8pmu_partition(cpu_pmu, reserved_host_counters); + else + pr_err("PMU partition is not supported"); + } =20 pmceid[0] =3D pmceid_raw[0] =3D read_pmceid0(); pmceid[1] =3D pmceid_raw[1] =3D read_pmceid1(); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 24a471cf59d56..95f404cdcb2df 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -47,7 +47,10 @@ struct arm_pmu_entry { struct arm_pmu *arm_pmu; }; =20 +extern bool armv8pmu_is_partitioned; + bool kvm_supports_guest_pmuv3(void); +bool has_host_pmu_partition_support(void); #define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >=3D 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); @@ -117,6 +120,11 @@ static inline bool kvm_supports_guest_pmuv3(void) return false; } =20 +static inline bool has_host_pmu_partition_support(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/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 52b37f7bdbf9e..f7b000bb3eca8 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -109,6 +109,7 @@ struct arm_pmu { */ int (*map_pmuv3_event)(unsigned int eventsel); DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS); + DECLARE_BITMAP(hw_cntr_mask, ARMPMU_MAX_HWEVENTS); bool secure_access; /* 32-bit ARM only */ struct platform_device *plat_device; struct pmu_hw_events __percpu *hw_events; @@ -129,6 +130,7 @@ struct arm_pmu { =20 /* Only to be used by ACPI probing code */ unsigned long acpi_cpuid; + int max_guest_counters; }; =20 #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oi1-f201.google.com (mail-oi1-f201.google.com [209.85.167.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5B2B8401489 for ; Mon, 4 May 2026 21:18:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929521; cv=none; b=EDmFOwkNQICjcmJ0u08ajHuMltkOQSb0/E4BrNXzR2kSNWUWjfO/0r8Si86VjNqg7GugzFsu4fLUcbbHaNm9ZUxQcqfs5ndUwoSP75v1h1jEY4RHd4e1VsBwxOkJO7PDA6P5VcT1Xyo2v8Ma5zOTI4arMrAeS1uWorc7wQagl5k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929521; c=relaxed/simple; bh=hs64zUVCH3HRa7wBllJ5k0yO+WABy4vlJLCxV5B6Fvw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=M4pX2n2d43VpPiIUrFTNMeESpukcT0+uxE53UsWSQu6o/wFft9az8D3LvCsv4QQ3CnNy0o9TgQKfroUJWoqGlx5Tq+x+61SciZeYcO4A9PMM+B39oB1x/Y+EjskyPCIlgCU7XuaLweFW6slQvSFjNQbHXk9LyDHPwSYV0LcPdpk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=JFdsxgIS; arc=none smtp.client-ip=209.85.167.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="JFdsxgIS" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-463a018f61bso3674642b6e.3 for ; Mon, 04 May 2026 14:18:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929517; x=1778534317; darn=vger.kernel.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=De6V46xDNQ/iwK8PV/vjnlYK6ThYLeVSMRlk50aLYFQ=; b=JFdsxgISSqbpHeGlQgB05LS+0pByCUmr1I/Dk6EqNvILGvrFxdfdCGRR8rRaeomdb/ TClsA80xtkdKFWJoFqfKZ93FjQAmbBZESSF+RxbbkWX9amfqryaTnAkbQzXmlygsiZB+ /lgrsabO8qdmCX8M7E/qU21H144YRvi01bAe8656i3f1SwfXf0tq9gogkUB2Wz3fwGTD MRD3RN2iEkA5Gu0ODg2ei+QMDSR/IA6+b+pOLOn97AAqp5xBXCXunL1kL8DdvrSL5TWk 9A91W4eRfSPDclnA+9EX/0WdR3LcvR6oZbtMaXjW2/E6kdW+HbLxOLuh5b2aJAxMwG/q TpKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929517; x=1778534317; 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=De6V46xDNQ/iwK8PV/vjnlYK6ThYLeVSMRlk50aLYFQ=; b=B3opHMNKaztiZcTU10nfdQEuUKJJHcMwUHixH002aAZnrGT9N9f4eO2ZnYQRBpIPPa pdBnnAdkMVxWrt+icpE/oyPtbSQERjiUjqwRqmOF/pGi9/t3i2K3S8lJWCZETIdpM6Z9 YBUFpP1f0WfqUp1CyDp+E9RDd0A3z114C9KxR3eSNc7v+slDjQqNUcbw8wKprkYjvJny 23WMQkpnxSArLxJTKWE/zfwlnqBCY0K2nvLpRZ8nJSsKETPwCGYaZ7aGl165nj3GCHjV Vi6uTiwbEZN+iBPq+aC/m2xbe+6ciHodenoHofzrxqTYYdC8dhZ5m4+z+h++pIpUx7+m 5KUw== X-Forwarded-Encrypted: i=1; AFNElJ+RWx3nGUaY2F6apzFHogyn98YjFnA0VI4v4SoCJcrdCDbJIPkqDwufcRlCoFoev8XW9TbZkQtSjI2b9O4=@vger.kernel.org X-Gm-Message-State: AOJu0YyMpLlRCGUTEsgMlZ2jNeHAIqOue0ZN0dj0dDA3qtj5M5ALIWGg sIc/j4TIjuDiOAMzb81ixbrAYI4/EvLb3IsIlg6jZ7YCUHHbpdRkacLYoib6wvm0/LmgK8NPKuI QJXjGZkg21kOPcP5dRFKzBT/cjA== X-Received: from japu20.prod.google.com ([2002:a02:cb94:0:b0:5d6:6508:34a5]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:f013:b0:67d:e85d:b5dc with SMTP id 006d021491bc7-6998dc3602cmr165172eaf.56.1777929516927; Mon, 04 May 2026 14:18:36 -0700 (PDT) Date: Mon, 4 May 2026 21:18:00 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-8-coltonlewis@google.com> Subject: [PATCH v7 07/20] KVM: arm64: Set up FGT for Partitioned PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" In order to gain the best performance benefit from partitioning the PMU, utilize fine grain traps (FEAT_FGT and FEAT_FGT2) to avoid trapping common PMU register accesses by the guest to remove that overhead. Untrapped: * PMCR_EL0 * PMUSERENR_EL0 * PMSELR_EL0 * PMCCNTR_EL0 * PMCNTEN_EL0 * PMINTEN_EL1 * PMEVCNTRn_EL0 These are safe to untrap because writing MDCR_EL2.HPMN as this series will do limits the effect of writes to any of these registers to the partition of counters 0..HPMN-1. Reads from these registers will not leak information from between guests as all these registers are context swapped by a later patch in this series. Reads from these registers also do not leak any information about the host's hardware beyond what is promised by PMUv3. Trapped: * PMOVS_EL0 * PMEVTYPERn_EL0 * PMCCFILTR_EL0 * PMICNTR_EL0 * PMICFILTR_EL0 * PMCEIDn_EL0 * PMMIR_EL1 PMOVS remains trapped so KVM can track overflow IRQs that will need to be injected into the guest. PMICNTR and PMIFILTR remain trapped because KVM is not handling them yet. PMEVTYPERn remains trapped so KVM can limit which events guests can count, such as disallowing counting at EL2. PMCCFILTR and PMCIFILTR are special cases of the same. PMCEIDn and PMMIR remain trapped because they can leak information specific to the host hardware implementation. NOTE: This patch temporarily forces kvm_vcpu_pmu_is_partitioned() to be false to prevent partial feature activation for easier debugging. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 4 +++ arch/arm64/kvm/config.c | 41 ++++++++++++++++++++++-- arch/arm64/kvm/pmu-direct.c | 54 ++++++++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 30 ++++++++++++++++++ 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index 154503f054886..42d62aa48d0a6 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -231,6 +231,10 @@ static inline bool kvm_set_pmuserenr(u64 val) } =20 static inline void kvm_vcpu_pmu_resync_el0(void) {} +static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu) +{ + return false; +} =20 /* PMU Version in DFR Register */ #define ARMV8_PMU_DFR_VER_NI 0 diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index d9f553cbf9dfd..3b5f028f5bf11 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -1598,12 +1598,47 @@ static void __compute_hfgwtr(struct kvm_vcpu *vcpu) *vcpu_fgt(vcpu, HFGWTR_EL2) |=3D HFGWTR_EL2_TCR_EL1; } =20 +static void __compute_hdfgrtr(struct kvm_vcpu *vcpu) +{ + __compute_fgt(vcpu, HDFGRTR_EL2); + + *vcpu_fgt(vcpu, HDFGRTR_EL2) |=3D + HDFGRTR_EL2_PMOVS + | HDFGRTR_EL2_PMCCFILTR_EL0 + | HDFGRTR_EL2_PMEVTYPERn_EL0 + | HDFGRTR_EL2_PMCEIDn_EL0 + | HDFGRTR_EL2_PMMIR_EL1; +} + static void __compute_hdfgwtr(struct kvm_vcpu *vcpu) { __compute_fgt(vcpu, HDFGWTR_EL2); =20 if (is_hyp_ctxt(vcpu)) *vcpu_fgt(vcpu, HDFGWTR_EL2) |=3D HDFGWTR_EL2_MDSCR_EL1; + + *vcpu_fgt(vcpu, HDFGWTR_EL2) |=3D + HDFGWTR_EL2_PMOVS + | HDFGWTR_EL2_PMCCFILTR_EL0 + | HDFGWTR_EL2_PMEVTYPERn_EL0; +} + +static void __compute_hdfgrtr2(struct kvm_vcpu *vcpu) +{ + __compute_fgt(vcpu, HDFGRTR2_EL2); + + *vcpu_fgt(vcpu, HDFGRTR2_EL2) &=3D + ~(HDFGRTR2_EL2_nPMICFILTR_EL0 + | HDFGRTR2_EL2_nPMICNTR_EL0); +} + +static void __compute_hdfgwtr2(struct kvm_vcpu *vcpu) +{ + __compute_fgt(vcpu, HDFGWTR2_EL2); + + *vcpu_fgt(vcpu, HDFGWTR2_EL2) &=3D + ~(HDFGWTR2_EL2_nPMICFILTR_EL0 + | HDFGWTR2_EL2_nPMICNTR_EL0); } =20 void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) @@ -1614,7 +1649,7 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) __compute_fgt(vcpu, HFGRTR_EL2); __compute_hfgwtr(vcpu); __compute_fgt(vcpu, HFGITR_EL2); - __compute_fgt(vcpu, HDFGRTR_EL2); + __compute_hdfgrtr(vcpu); __compute_hdfgwtr(vcpu); __compute_fgt(vcpu, HAFGRTR_EL2); =20 @@ -1624,6 +1659,6 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) __compute_fgt(vcpu, HFGRTR2_EL2); __compute_fgt(vcpu, HFGWTR2_EL2); __compute_fgt(vcpu, HFGITR2_EL2); - __compute_fgt(vcpu, HDFGRTR2_EL2); - __compute_fgt(vcpu, HDFGWTR2_EL2); + __compute_hdfgrtr2(vcpu); + __compute_hdfgwtr2(vcpu); } diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 74e40e4915416..2148bc46079c4 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -5,6 +5,8 @@ */ =20 #include +#include +#include =20 #include =20 @@ -20,3 +22,55 @@ bool has_host_pmu_partition_support(void) return has_vhe() && system_supports_pmuv3(); } + +/** + * kvm_pmu_is_partitioned() - Determine if given PMU is partitioned + * @pmu: Pointer to arm_pmu struct + * + * Determine if given PMU is partitioned by looking at hpmn field. The + * PMU is partitioned if this field is less than the number of + * counters in the system. + * + * Return: True if the PMU is partitioned, false otherwise + */ +bool kvm_pmu_is_partitioned(struct arm_pmu *pmu) +{ + if (!pmu) + return false; + + return pmu->max_guest_counters >=3D 0 && + pmu->max_guest_counters <=3D *host_data_ptr(nr_event_counters); +} + +/** + * kvm_vcpu_pmu_is_partitioned() - Determine if given VCPU has a partition= ed PMU + * @vcpu: Pointer to kvm_vcpu struct + * + * Determine if given VCPU has a partitioned PMU by extracting that + * field and passing it to :c:func:`kvm_pmu_is_partitioned` + * + * Return: True if the VCPU PMU is partitioned, false otherwise + */ +bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu) +{ + return kvm_pmu_is_partitioned(vcpu->kvm->arch.arm_pmu) && + false; +} + +/** + * kvm_vcpu_pmu_use_fgt() - Determine if we can use FGT + * @vcpu: Pointer to struct kvm_vcpu + * + * Determine if we can use FGT for direct access to registers. We can + * if capabilities permit the number of guest counters requested. + * + * Return: True if we can use FGT, false otherwise + */ +bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu) +{ + u8 hpmn =3D vcpu->kvm->arch.nr_pmu_counters; + + return kvm_vcpu_pmu_is_partitioned(vcpu) && + cpus_have_final_cap(ARM64_HAS_FGT) && + (hpmn !=3D 0 || cpus_have_final_cap(ARM64_HAS_HPMN0)); +} diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 95f404cdcb2df..c3987e0c01775 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -92,6 +92,23 @@ void kvm_vcpu_pmu_resync_el0(void); #define kvm_vcpu_has_pmu(vcpu) \ (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3)) =20 +bool kvm_pmu_is_partitioned(struct arm_pmu *pmu); + +#if !defined(__KVM_NVHE_HYPERVISOR__) +bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu); +bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu); +#else +static inline bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu) +{ + return false; +} + +static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu) +{ + return false; +} +#endif + /* * Updates the vcpu's view of the pmu events for this cpu. * Must be called before every vcpu run after disabling interrupts, to ens= ure @@ -131,6 +148,14 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm= _vcpu *vcpu, { return 0; } +static inline bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu) +{ + return false; +} +static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu) +{ + return false; +} static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) {} static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, @@ -228,6 +253,11 @@ static inline bool kvm_pmu_counter_is_hyp(struct kvm_v= cpu *vcpu, unsigned int id =20 static inline void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) {} =20 +static inline bool kvm_pmu_is_partitioned(void *pmu) +{ + return false; +} + #endif =20 #endif --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f73.google.com (mail-ot1-f73.google.com [209.85.210.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D323634750E for ; Mon, 4 May 2026 21:18:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929523; cv=none; b=ZDBYwtQ7lYmOLhEFsbDJwVJBTDU+bUw9tkGN6WYo7GDdYVqCS6wKV7vBtvM/OTZ2eYjSGqsCawt59xiDe/apyWBRUgx0+5O9lWoLBEnUte3c8JeRBofnF38sQeN+kyDcPsc6n7mwyyJZyQ+49Sn4J+A3wEC7j/QJ5XaOqumwxS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929523; c=relaxed/simple; bh=SKo63HjoGCHTOKU92BPiu/k9cKpX9pAJWhd5TqUrBa0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=H+uyLk3XAtujAHAGKALesR3Fw7MQy6EVhk1b70ikKU6GQMU9GmmoNW2XEqlbWCXIzJc0HWe2DLUKR7sIVwdXXFdM+xmFPkmER6P+2EdIjvQoosY1aO8dBw2aP5/dpR83T03NYASyIJEOvwzb7gx4dVajeKZsQTbSqLz7RrJv8Js= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SScHX28L; arc=none smtp.client-ip=209.85.210.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SScHX28L" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7dbd50dee52so11811626a34.2 for ; Mon, 04 May 2026 14:18:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929518; x=1778534318; darn=vger.kernel.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=BdEaEz514fBiVElpHNtxsUX8/xlNYtbOJ74nBbxAmPE=; b=SScHX28L2iiptBatsfhGJhlVP0VDMYhU4KRwD8PqfTk2ewgteDQDui4Muja7O2Nh6L Cc6hr04b5t6KrrWrDJ3nihb2vfjg5VGLvjS52nR7QO48kGaUl68TqCGQwO9mnj1hlOJK I7mbOfrzeMSVL7RnWPQpPaAM19ulaszOS8zislGUo20sCgVH29vbII6csDGPcCClfQW6 +AeW0vr97awtvHZGHPSjPYrUMCp9QgRUnEJtNxe/n6yakNXTib69L3Rc2jy2JbRiC3/l Ws7h0kKdIhVO95XS+KgxoV061Efqym0aI8AUPFJWsJPrDqpJ4BY9xVeAlxWZpjUn5v0f Nf7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929518; x=1778534318; 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=BdEaEz514fBiVElpHNtxsUX8/xlNYtbOJ74nBbxAmPE=; b=ALmR2RdL4M1ELTBmxCbjpBtDeBy3OC+wUbi+M7OMy4NyCvpQrGoeDkv5Q+M+4iHsGV DxlziNoLxHycR+WbvVwA+GiJLdI6EF3gjRjWvW2GQiWLylqe9lx5dqvavPDuoQyEoTep 8qmpTSBtQR1GVEVzkQYoUXVHDiRvLe38n/LWCrSjiuML5WXLoHOWe5B916mwt4aoyEGn ZelyOXTlQGqzS2xXp7h992NTNjPtEvL1YstCSYBtuHPPyf1Td5BsK9A5O8J9nrWvIyEm AOKjr5QSv1HXVal8sfOVYtpkMzEZndIdXY5pJUAFjo+hjUrUPPnwr5lTEppHpxPTWQbk /AXg== X-Forwarded-Encrypted: i=1; AFNElJ/lqyF+WngPAL0p4myS0yiifkuLGMZSatyGWCMMpFBNZ3SATWp7ssYs9iVtrVU0bVoxsiNwZ4O/TAZ1UXM=@vger.kernel.org X-Gm-Message-State: AOJu0YzELUHWdIUZYH24egymOnnv8q+tD4QsAjqRtItQ5b6hF/no7uSm Yujd8v0MXecbfMVbEdyzOZLXDfVydMr1xlnzIh7MXpE1yP2wcdGzE1qj2FFGLYT2e3GE6ciEz56 cwvz8yQoGrKpdWAvCRhbJHN5cuw== X-Received: from ilvd7.prod.google.com ([2002:a05:6e02:2147:b0:4fa:a7a5:6571]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2214:b0:696:1791:add8 with SMTP id 006d021491bc7-69697d5e366mr5834496eaf.40.1777929517855; Mon, 04 May 2026 14:18:37 -0700 (PDT) Date: Mon, 4 May 2026 21:18:01 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-9-coltonlewis@google.com> Subject: [PATCH v7 08/20] KVM: arm64: Add Partitioned PMU register trap handlers From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" We may want a partitioned PMU but not have FEAT_FGT to untrap the specific registers that would normally be untrapped. Add handling for those trapped register accesses that does the right thing if the PMU is partitioned. For registers that shouldn't be written to hardware because they require special handling (PMEVTYPER and PMOVS), write to the virtual register. A later patch will ensure these are handled correctly at vcpu_load time. Signed-off-by: Colton Lewis --- arch/arm64/kvm/sys_regs.c | 236 +++++++++++++++++++++++++++++++------- 1 file changed, 197 insertions(+), 39 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 0a8e8ee69cd00..cc3d1804ab200 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -985,9 +985,25 @@ static u64 reset_pmcr(struct kvm_vcpu *vcpu, const str= uct sys_reg_desc *r) return __vcpu_sys_reg(vcpu, r->reg); } =20 +static void pmu_write_pmuserenr(struct kvm_vcpu *vcpu, u64 val) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + write_sysreg(val, pmuserenr_el0); + else + __vcpu_assign_sys_reg(vcpu, PMUSERENR_EL0, val); +} + +static u64 pmu_read_pmuserenr(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + return read_sysreg(pmuserenr_el0); + else + return __vcpu_sys_reg(vcpu, PMUSERENR_EL0); +} + static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags) { - u64 reg =3D __vcpu_sys_reg(vcpu, PMUSERENR_EL0); + u64 reg =3D pmu_read_pmuserenr(vcpu); bool enabled =3D (reg & flags) || vcpu_mode_priv(vcpu); =20 if (!enabled) @@ -1016,6 +1032,29 @@ static bool pmu_access_event_counter_el0_disabled(st= ruct kvm_vcpu *vcpu) return check_pmu_access_disabled(vcpu, ARMV8_PMU_USERENR_ER | ARMV8_PMU_U= SERENR_EN); } =20 +static void pmu_write_pmcr(struct kvm_vcpu *vcpu, u64 val) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + write_sysreg(val, pmcr_el0); + return; + } + + kvm_pmu_handle_pmcr(vcpu, val); +} + +static u64 pmu_read_pmcr(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + return u64_replace_bits( + read_sysreg(pmcr_el0), + vcpu->kvm->arch.nr_pmu_counters, + ARMV8_PMU_PMCR_N); + } + + return kvm_vcpu_read_pmcr(vcpu); + +} + static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { @@ -1026,18 +1065,17 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, stru= ct sys_reg_params *p, =20 if (p->is_write) { /* - * Only update writeable bits of PMCR (continuing into - * kvm_pmu_handle_pmcr() as well) + * Only update writeable bits of PMCR */ - val =3D kvm_vcpu_read_pmcr(vcpu); + val =3D pmu_read_pmcr(vcpu); val &=3D ~ARMV8_PMU_PMCR_MASK; val |=3D p->regval & ARMV8_PMU_PMCR_MASK; if (!kvm_supports_32bit_el0()) val |=3D ARMV8_PMU_PMCR_LC; - kvm_pmu_handle_pmcr(vcpu, val); + pmu_write_pmcr(vcpu, val); } else { /* PMCR.P & PMCR.C are RAZ */ - val =3D kvm_vcpu_read_pmcr(vcpu) + val =3D pmu_read_pmcr(vcpu) & ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C); p->regval =3D val; } @@ -1045,6 +1083,24 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struc= t sys_reg_params *p, return true; } =20 +static void pmu_write_pmselr(struct kvm_vcpu *vcpu, u64 val) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + write_sysreg(val, pmselr_el0); + return; + } + + __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, val); +} + +static u64 pmu_read_pmselr(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + return read_sysreg(pmselr_el0); + + return __vcpu_sys_reg(vcpu, PMSELR_EL0); +} + static bool access_pmselr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { @@ -1052,10 +1108,10 @@ static bool access_pmselr(struct kvm_vcpu *vcpu, st= ruct sys_reg_params *p, return false; =20 if (p->is_write) - __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, p->regval); + pmu_write_pmselr(vcpu, p->regval); else /* return PMSELR.SEL field */ - p->regval =3D __vcpu_sys_reg(vcpu, PMSELR_EL0) + p->regval =3D pmu_read_pmselr(vcpu) & PMSELR_EL0_SEL_MASK; =20 return true; @@ -1128,6 +1184,44 @@ static int set_pmu_evcntr(struct kvm_vcpu *vcpu, con= st struct sys_reg_desc *r, return 0; } =20 +static void pmu_write_evcntr(struct kvm_vcpu *vcpu, u64 val, u64 idx) +{ + u64 pmselr; + + if (!kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_counter_value(vcpu, idx, val); + return; + } + + if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) { + write_sysreg(val, pmccntr_el0); + return; + } + + pmselr =3D read_sysreg(pmselr_el0); + write_sysreg(idx, pmselr_el0); + write_sysreg(val, pmxevcntr_el0); + write_sysreg(pmselr, pmselr_el0); +} + +static u64 pmu_read_evcntr(struct kvm_vcpu *vcpu, u64 idx) +{ + u64 pmselr; + u64 val; + + if (!kvm_vcpu_pmu_is_partitioned(vcpu)) + return kvm_pmu_get_counter_value(vcpu, idx); + + if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) + return read_sysreg(pmccntr_el0); + + pmselr =3D read_sysreg(pmselr_el0); + write_sysreg(idx, pmselr_el0); + val =3D read_sysreg(pmxevcntr_el0); + write_sysreg(pmselr, pmselr_el0); + return val; +} + static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) @@ -1141,7 +1235,7 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, return false; =20 idx =3D SYS_FIELD_GET(PMSELR_EL0, SEL, - __vcpu_sys_reg(vcpu, PMSELR_EL0)); + pmu_read_pmselr(vcpu)); } else if (r->Op2 =3D=3D 0) { /* PMCCNTR_EL0 */ if (pmu_access_cycle_counter_el0_disabled(vcpu)) @@ -1173,14 +1267,34 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, if (pmu_access_el0_disabled(vcpu)) return false; =20 - kvm_pmu_set_counter_value(vcpu, idx, p->regval); + pmu_write_evcntr(vcpu, p->regval, idx); } else { - p->regval =3D kvm_pmu_get_counter_value(vcpu, idx); + p->regval =3D pmu_read_evcntr(vcpu, idx); } =20 return true; } =20 + +static void pmu_write_evtyper(struct kvm_vcpu *vcpu, u64 val, u64 idx) +{ + u64 mask; + + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + mask =3D kvm_pmu_evtyper_mask(vcpu->kvm); + __vcpu_assign_sys_reg(vcpu, PMEVTYPER0_EL0 + idx, val & mask); + return; + } + + kvm_pmu_set_counter_event_type(vcpu, val, idx); + kvm_vcpu_pmu_restore_guest(vcpu); +} + +static u64 pmu_read_evtyper(struct kvm_vcpu *vcpu, u64 idx) +{ + return __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + idx); +} + static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_param= s *p, const struct sys_reg_desc *r) { @@ -1191,7 +1305,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu,= struct sys_reg_params *p, =20 if (r->CRn =3D=3D 9 && r->CRm =3D=3D 13 && r->Op2 =3D=3D 1) { /* PMXEVTYPER_EL0 */ - idx =3D SYS_FIELD_GET(PMSELR_EL0, SEL, __vcpu_sys_reg(vcpu, PMSELR_EL0)); + idx =3D SYS_FIELD_GET(PMSELR_EL0, SEL, pmu_read_pmselr(vcpu)); reg =3D PMEVTYPER0_EL0 + idx; } else if (r->CRn =3D=3D 14 && (r->CRm & 12) =3D=3D 12) { idx =3D ((r->CRm & 3) << 3) | (r->Op2 & 7); @@ -1207,12 +1321,10 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcp= u, struct sys_reg_params *p, if (!pmu_counter_idx_valid(vcpu, idx)) return false; =20 - if (p->is_write) { - kvm_pmu_set_counter_event_type(vcpu, p->regval, idx); - kvm_vcpu_pmu_restore_guest(vcpu); - } else { - p->regval =3D __vcpu_sys_reg(vcpu, reg); - } + if (p->is_write) + pmu_write_evtyper(vcpu, p->regval, idx); + else + p->regval =3D pmu_read_evtyper(vcpu, idx); =20 return true; } @@ -1235,6 +1347,35 @@ static int get_pmreg(struct kvm_vcpu *vcpu, const st= ruct sys_reg_desc *r, u64 *v return 0; } =20 +static void pmu_write_pmcnten(struct kvm_vcpu *vcpu, u64 val, bool set) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + if (set) + write_sysreg(val, pmcntenset_el0); + else + write_sysreg(val, pmcntenclr_el0); + + return; + } + + if (set) + /* accessing PMCNTENSET_EL0 */ + __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, |=3D, val); + else + /* accessing PMCNTENCLR_EL0 */ + __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, &=3D, ~val); + + kvm_pmu_reprogram_counter_mask(vcpu, val); +} + +static u64 pmu_read_pmcnten(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + return read_sysreg(pmcntenset_el0); + + return __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); +} + static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { @@ -1246,40 +1387,58 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, s= truct sys_reg_params *p, mask =3D kvm_pmu_accessible_counter_mask(vcpu); if (p->is_write) { val =3D p->regval & mask; - if (r->Op2 & 0x1) - /* accessing PMCNTENSET_EL0 */ - __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, |=3D, val); - else - /* accessing PMCNTENCLR_EL0 */ - __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, &=3D, ~val); - - kvm_pmu_reprogram_counter_mask(vcpu, val); + pmu_write_pmcnten(vcpu, val, r->Op2 & 0x1); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); + p->regval =3D pmu_read_pmcnten(vcpu); } =20 return true; } =20 +static void pmu_write_pminten(struct kvm_vcpu *vcpu, u64 val, bool set) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + if (set) + write_sysreg(val, pmintenset_el1); + else + write_sysreg(val, pmintenclr_el1); + + return; + } + + if (set) + /* accessing PMINTENSET_EL1 */ + __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, |=3D, val); + else + /* accessing PMINTENCLR_EL1 */ + __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=3D, ~val); + + kvm_pmu_reprogram_counter_mask(vcpu, val); +} + +static u64 pmu_read_pminten(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + return read_sysreg(pmintenset_el1); + + return __vcpu_sys_reg(vcpu, PMINTENSET_EL1); +} + static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { - u64 mask =3D kvm_pmu_accessible_counter_mask(vcpu); + u64 val, mask; =20 if (check_pmu_access_disabled(vcpu, 0)) return false; =20 + mask =3D kvm_pmu_accessible_counter_mask(vcpu); if (p->is_write) { - u64 val =3D p->regval & mask; + val =3D p->regval & mask; =20 - if (r->Op2 & 0x1) - /* accessing PMINTENSET_EL1 */ - __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, |=3D, val); - else - /* accessing PMINTENCLR_EL1 */ - __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=3D, ~val); + pmu_write_pminten(vcpu, val, r->Op2 & 0x1); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMINTENSET_EL1); + p->regval =3D pmu_read_pminten(vcpu); } =20 return true; @@ -1330,10 +1489,9 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, = struct sys_reg_params *p, if (!vcpu_mode_priv(vcpu)) return undef_access(vcpu, p, r); =20 - __vcpu_assign_sys_reg(vcpu, PMUSERENR_EL0, - (p->regval & ARMV8_PMU_USERENR_MASK)); + pmu_write_pmuserenr(vcpu, p->regval & ARMV8_PMU_USERENR_MASK); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMUSERENR_EL0) + p->regval =3D pmu_read_pmuserenr(vcpu) & ARMV8_PMU_USERENR_MASK; } =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f73.google.com (mail-oo1-f73.google.com [209.85.161.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1B4E402452 for ; Mon, 4 May 2026 21:18:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929524; cv=none; b=L8akpnkziL3HlNSRBPCS+kyQDSOC5GaWfSjj6kbuVWgDwdiperc6egpai8Yo7f02gi70U0Q9/H02PJnzjwf9c/QMkkZ+1LS+/DDNF/2d9SeBYB5iKssXk5+nWVRA0n+0uQ9vYvIbPkedD+sqyQmr185noIjzEkeNA3S6oBKWt6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929524; c=relaxed/simple; bh=Ju4hEvbWQIxue7C0GzlmuOc4hIQ+zS118FGwzfP52DY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ipW/TSU+knNmSSgXk4YW0CnLgeOSCGYIMV4VnDSOSF/uOSSFswxhBPtEBwFYOVsyUGVC6Tehp8MK1kzeamCtToOiZYTp1uOsS1HCPgqRell8ppdQNsKeNdj8ZsFqedd8CpWC3Uns+M6sQ6wzLvM33CN1Yt1lPnkOtbQK2E0DqEk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=m21tjqFt; arc=none smtp.client-ip=209.85.161.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="m21tjqFt" Received: by mail-oo1-f73.google.com with SMTP id 006d021491bc7-694867ca29cso9398504eaf.2 for ; Mon, 04 May 2026 14:18:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929519; x=1778534319; darn=vger.kernel.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=2uXhxMRnzPluCmaQoLE1tgsrLMP2Ws4KNKFg4hBQdkY=; b=m21tjqFtASnNVjvZiZ1EiD4gwYhBnmuwpOax7H+OWTZaG5aZsk8yVTYCkMUkU38ikt B9eXHuULytkOwW+FGCwbU3ico7IvsLtsksdxl8otMmeMfzGcW3O/VLOY4Ud098jdTMli cwWzUT4SxhNbiyianVOMVQu6XeRZq4Ec+A4MneE+4HBNjh1Rdn9semlGPd4Kwy4ncOru aOFE5RVYHOz51iU5KRcVu3zcYW4dNYbmpLQijCHPX1hckE196iDRr2GVntxCvWEytXYJ F2m3M0fvKa9QRSLJMv6KTZzg6RUMQ+5RD+hU6EFQTrbgXzOnmsSgCw0/QNu3CvHF4hwj 0kGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929519; x=1778534319; 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=2uXhxMRnzPluCmaQoLE1tgsrLMP2Ws4KNKFg4hBQdkY=; b=gMED8hX9kChT63GrV6mashTO0UWVvER64bai+Z/Fxi14MhTvLBAurgyDxk/DvKNgxd wLPLCfertqbC0FmTJfIkf1Sg8/tTcq4BOQ66CoqFwcQRV/4vevbiDcfyvTuOv7UZwQcs BYB5v46kafdAyCPIVyt/0cpZxafdnRjjjm1L2uTD6uA86r5tcTAXDx992pDyrL2CLnHc t8NzFpdlxf1f3fkkYOl1C656c2aJ+mCAXkcD1Fy5VNm/+E5bN5tqCHGpVYoBKAUx9jJf W6M14Nq8SxkOSPNkGxKtEE9WNAeQ5GR2L7Nka/Ly+y6Mmha87YS8yLS67sb06BwBlMIZ mEPw== X-Forwarded-Encrypted: i=1; AFNElJ94o0G+S1I8a42rW8p1NCHnBpujgEzkoGt0vSRETNwh7gRufYQUwVYeLMN2nmXAxH4Z2po+oDh3G4OpVik=@vger.kernel.org X-Gm-Message-State: AOJu0YzKnCe/2r7C/ZnbBHzm/0Tc1QKzyUg2Py1t1CZU40CByXh4D3AF e7m/d1Mt4sTSOh/YHh/Y54n7AJI/ve8hPjR9RNR6xwHyZkuU5Uo/fhIsR98MOdv5vfdz3TiKnwA MA0Pi/wxwxP7c1jdHG9cbfR46LA== X-Received: from ilsi9.prod.google.com ([2002:a05:6e02:549:b0:4fe:73ca:fda6]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a4a:e911:0:b0:684:188a:737d with SMTP id 006d021491bc7-6998d257e73mr177660eaf.44.1777929518793; Mon, 04 May 2026 14:18:38 -0700 (PDT) Date: Mon, 4 May 2026 21:18:02 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-10-coltonlewis@google.com> Subject: [PATCH v7 09/20] KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Set up MDCR_EL2 to handle a Partitioned PMU. That means calculate an appropriate value for HPMN instead of the default maximum setting the host allows (which implies no partition) so hardware enforces that a guest will only see the counters in the guest partition. Setting HPMN to a non default value means the global enable bit for the host counters is now MDCR_EL2.HPME instead of the usual PMCR_EL0.E. Enable the HPME bit to allow the host to count guest events. Since HPME only has an effect when HPMN is set which we only do for the guest, it is correct to enable it unconditionally here. Unset the TPM and TPMCR bits, which trap all PMU accesses, if FGT (fine grain trapping) is being used. If available, set the filtering bits HPMD and HCCD to be extra sure nothing in the guest counts at EL2. Signed-off-by: Colton Lewis --- arch/arm64/kvm/debug.c | 29 ++++++++++++++++++++++++++--- arch/arm64/kvm/pmu-direct.c | 24 ++++++++++++++++++++++++ arch/arm64/kvm/pmu.c | 7 +++++++ include/kvm/arm_pmu.h | 11 +++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index 3ad6b7c6e4ba7..0ab89c91e19cb 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -36,20 +36,43 @@ static int cpu_has_spe(u64 dfr0) */ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) { + int hpmn =3D kvm_pmu_hpmn(vcpu); + preempt_disable(); =20 /* * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK * to disable guest access to the profiling and trace buffers */ - vcpu->arch.mdcr_el2 =3D FIELD_PREP(MDCR_EL2_HPMN, - *host_data_ptr(nr_event_counters)); + + vcpu->arch.mdcr_el2 =3D FIELD_PREP(MDCR_EL2_HPMN, hpmn); vcpu->arch.mdcr_el2 |=3D (MDCR_EL2_TPM | MDCR_EL2_TPMS | MDCR_EL2_TTRF | MDCR_EL2_TPMCR | MDCR_EL2_TDRA | - MDCR_EL2_TDOSA); + MDCR_EL2_TDOSA | + MDCR_EL2_HPME); + + if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + /* + * Filtering these should be redundant because we trap + * all the TYPER and FILTR registers anyway and ensure + * they filter EL2, but set the bits if they are here. + */ + if (is_pmuv3p1(read_pmuver())) + vcpu->arch.mdcr_el2 |=3D MDCR_EL2_HPMD; + if (is_pmuv3p5(read_pmuver())) + vcpu->arch.mdcr_el2 |=3D MDCR_EL2_HCCD; + + /* + * Take out the coarse grain traps if we are using + * fine grain traps. + */ + if (kvm_vcpu_pmu_use_fgt(vcpu)) + vcpu->arch.mdcr_el2 &=3D ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR); + + } =20 /* Is the VM being debugged by userspace? */ if (vcpu->guest_debug) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 2148bc46079c4..63ac72910e4b5 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -74,3 +74,27 @@ bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu) cpus_have_final_cap(ARM64_HAS_FGT) && (hpmn !=3D 0 || cpus_have_final_cap(ARM64_HAS_HPMN0)); } + +/** + * kvm_pmu_hpmn() - Calculate HPMN field value + * @vcpu: Pointer to struct kvm_vcpu + * + * Calculate the appropriate value to set for MDCR_EL2.HPMN. If + * partitioned, this is the number of counters set for the guest if + * supported, falling back to max_guest_counters if needed. If we are not + * partitioned or can't set the implied HPMN value, fall back to the + * host value. + * + * Return: A valid HPMN value + */ +u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) +{ + u8 nr_guest_cntr =3D vcpu->kvm->arch.nr_pmu_counters; + + if (kvm_vcpu_pmu_is_partitioned(vcpu) + && !vcpu_on_unsupported_cpu(vcpu) + && (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0)) + return nr_guest_cntr; + + return *host_data_ptr(nr_event_counters); +} diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index ee2f0f7e61bcf..8c10ad05661bc 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -542,6 +542,13 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm) if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) return 1; =20 + /* + * If partitioned then we are limited by the max counters in + * the guest partition. + */ + if (kvm_pmu_is_partitioned(arm_pmu)) + return arm_pmu->max_guest_counters; + /* * The arm_pmu->cntr_mask considers the fixed counter(s) as well. * Ignore those and return only the general-purpose counters. diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index c3987e0c01775..6aaeb27642540 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -94,6 +94,9 @@ void kvm_vcpu_pmu_resync_el0(void); =20 bool kvm_pmu_is_partitioned(struct arm_pmu *pmu); =20 +u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu); +u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu); + #if !defined(__KVM_NVHE_HYPERVISOR__) bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu); bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu); @@ -156,6 +159,14 @@ static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcp= u *vcpu) { return false; } +static inline u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu) +{ + return 0; +} +static inline u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) +{ + return 0; +} static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) {} static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oa1-f74.google.com (mail-oa1-f74.google.com [209.85.160.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 00221406284 for ; Mon, 4 May 2026 21:18:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929527; cv=none; b=qH+rYP69Uu4KrpAk+9gbBU0P/LRNmk6Si/fHG4RFDM54qNE3EYeXsxXbIZYeQ0B4LsVbvXXP7dLd2DMKjhC1D7MYH8/etF09lOPhbhihfYkzuxEzR36IJ054rbkWIswBdmXfMrjyEdoTwgQ39XksanbZEogwIHb+nd2mSuiKofs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929527; c=relaxed/simple; bh=QP8yK+xtQLGuqO+G6G0f6iW6TMzPpPpuwA/KTPen/9Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pcWVhOzl46ahshSZHLdGukXBBoVmOQ1ZToOmzVq1/8F6q9z69vfAS3YtEzE+FoINUUF0GgPB8TqvDufrZD31akyulYb4/7OhYWqMpHdE6Y+DQhZLcx6JtXn0e0xnAQXegCBDH9Sh3Wk43TOd1XNzQTM6cAIEhxRn+V59q5qOYaM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=QlmXfI6U; arc=none smtp.client-ip=209.85.160.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="QlmXfI6U" Received: by mail-oa1-f74.google.com with SMTP id 586e51a60fabf-42fb0f12b10so5334203fac.2 for ; Mon, 04 May 2026 14:18:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929520; x=1778534320; darn=vger.kernel.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=4FfWFLRUcTJ7zqTosU1ICG6npkIxsQ1895Jbe5/J8PE=; b=QlmXfI6UVeBMYTeHwhjmAiSrRxiDpZWm+x7LOLQsFSEmNDqTx0IhgkuqMcQ6wCjL3v YzF1EeBmnQV5G6gWnnYHkr3+B6lTkP5oW86y+9XokF8sVu96DxhOYHlOtHt6W7YJ11kO /b066zFxfCqr3XsJ9HktoaDOFOYKQbCpnUF/qUMYtM3ocI+9fAhA2NrQ0NKUjNjZSPkP PGhzK7kFArAKhSeuJ+uCruE16DFvgMieZS9tGZWbOQKV/t2hkzyUqmuQXq5m4p9povzI 6/m36z4WLduYxbAxm9o+MDZpoSllBRLUGPeckOYP2HEPESJTytFb1LCBeknkc/baNi86 TQHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929520; x=1778534320; 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=4FfWFLRUcTJ7zqTosU1ICG6npkIxsQ1895Jbe5/J8PE=; b=Eo+NB7v8SNZiXufsm+l56bY1UlPOVuI82L9xuhDOs334KnBmMoHHVxH28zoZp5cKx8 I5jPh/tiAC6YsHnAN2VJUeFkmDY7ZtYtRhqUuop+js8TecSluVOFolWnpGUnk6SaldUt etklnemXAih7vVbh/VaueBtfI8kw8gMjd9PUJNCWsi+dpNV71LWD4EvCyPfuAoj3I8sa J0S1Jiftd19oG7/GjNqW0CwfzuhT4Md7KM+lHvJsQnE0stdpJAlbIQHAt6APyzoSM64Q kz+VJ2Z0goGltgyw1n2Guhf2FwAyRPO4/sAeYRTB80UKYzSoEfRsZAVZSkD5x0a3PfGL pJoA== X-Forwarded-Encrypted: i=1; AFNElJ+0sbLk+w1o+00Fh18VoJvwSN1TRqMlmQLv/IeQ49RotLuFlpn92wEcsLPBO2lIcgE/xbJzy+ELTDG9LHI=@vger.kernel.org X-Gm-Message-State: AOJu0Yy/YNYxvFxqZOFh0hv5v1loYnlHR9B9D82m+Ai427HgymKU+sDs Mu48bARZiEYpfSts8WHn4dfOgBIh0tito8H0fvecHhmTtAV8lI0tveCVeiqHO+3aRdiLlhLJul0 HAXle+umG+q30bfFdWvFa2Ppj2g== X-Received: from iobim21.prod.google.com ([2002:a05:6602:6d15:b0:967:e08a:567c]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a4a:e914:0:b0:696:573c:9f12 with SMTP id 006d021491bc7-6998a56fa40mr176153eaf.4.1777929520238; Mon, 04 May 2026 14:18:40 -0700 (PDT) Date: Mon, 4 May 2026 21:18:03 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-11-coltonlewis@google.com> Subject: [PATCH v7 10/20] KVM: arm64: Context swap Partitioned PMU guest registers From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Save and restore newly untrapped registers that can be directly accessed by the guest when the PMU is partitioned. * PMEVCNTRn_EL0 * PMCCNTR_EL0 * PMSELR_EL0 * PMCR_EL0 * PMCNTEN_EL0 * PMINTEN_EL1 If we know we are not partitioned (that is, using the emulated vPMU), then return immediately. A later patch will make this lazy so the context swaps don't happen unless the guest has accessed the PMU. PMEVTYPER is handled in a following patch since we must apply the KVM event filter before writing values to hardware. PMOVS guest counters are cleared to avoid the possibility of generating spurious interrupts when PMINTEN is written. This is fine because the virtual register for PMOVS is always the canonical value. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 4 + arch/arm64/kvm/arm.c | 2 + arch/arm64/kvm/pmu-direct.c | 169 +++++++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 16 +++ 4 files changed, 191 insertions(+) diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index 42d62aa48d0a6..eebc89bdab7a1 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -235,6 +235,10 @@ static inline bool kvm_pmu_is_partitioned(struct arm_p= mu *pmu) { return false; } +static inline u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu) +{ + return ~0; +} =20 /* PMU Version in DFR Register */ #define ARMV8_PMU_DFR_VER_NI 0 diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 410ffd41fd73a..a942f2bc13fc4 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -680,6 +680,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) kvm_vcpu_load_vhe(vcpu); kvm_arch_vcpu_load_fp(vcpu); kvm_vcpu_pmu_restore_guest(vcpu); + kvm_pmu_load(vcpu); if (kvm_arm_is_pvtime_enabled(&vcpu->arch)) kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); =20 @@ -721,6 +722,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) kvm_timer_vcpu_put(vcpu); kvm_vgic_put(vcpu); kvm_vcpu_pmu_restore_host(vcpu); + kvm_pmu_put(vcpu); if (vcpu_has_nv(vcpu)) kvm_vcpu_put_hw_mmu(vcpu); kvm_arm_vmid_clear_active(); diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 63ac72910e4b5..360d022d918d5 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -9,6 +9,7 @@ #include =20 #include +#include =20 /** * has_host_pmu_partition_support() - Determine if partitioning is possible @@ -98,3 +99,171 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) =20 return *host_data_ptr(nr_event_counters); } + +/** + * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved counters + * @pmu: Pointer to arm_pmu struct + * + * Compute the bitmask that selects the host-reserved counters in the + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters + * in HPMN..N + * + * Return: Bitmask + */ +u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu) +{ + u8 nr_counters =3D *host_data_ptr(nr_event_counters); + + if (kvm_pmu_is_partitioned(pmu)) + return GENMASK(nr_counters - 1, pmu->max_guest_counters); + + return ARMV8_PMU_CNT_MASK_ALL; +} + +/** + * kvm_pmu_guest_counter_mask() - Compute bitmask of guest-reserved counte= rs + * @pmu: Pointer to arm_pmu struct + * + * Compute the bitmask that selects the guest-reserved counters in the + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters + * in 0..HPMN and the cycle and instruction counters. + * + * Return: Bitmask + */ +u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu) +{ + if (kvm_pmu_is_partitioned(pmu)) + return ARMV8_PMU_CNT_MASK_C | GENMASK(pmu->max_guest_counters - 1, 0); + + return 0; +} + +/** + * kvm_pmu_load() - Load untrapped PMU registers + * @vcpu: Pointer to struct kvm_vcpu + * + * Load all untrapped PMU registers from the VCPU into the PCPU. Mask + * to only bits belonging to guest-reserved counters and leave + * host-reserved counters alone in bitmask registers. + */ +void kvm_pmu_load(struct kvm_vcpu *vcpu) +{ + struct arm_pmu *pmu; + unsigned long guest_counters; + u64 mask; + u8 i; + u64 val; + + /* + * If we aren't guest-owned then we know the guest isn't using + * the PMU anyway, so no need to bother with the swap. + */ + if (!kvm_vcpu_pmu_is_partitioned(vcpu)) + return; + + preempt_disable(); + + pmu =3D vcpu->kvm->arch.arm_pmu; + guest_counters =3D kvm_pmu_guest_counter_mask(pmu); + + for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) { + val =3D __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i); + + if (i =3D=3D ARMV8_PMU_CYCLE_IDX) { + write_sysreg(val, pmccntr_el0); + } else { + write_sysreg(i, pmselr_el0); + write_sysreg(val, pmxevcntr_el0); + } + } + + val =3D __vcpu_sys_reg(vcpu, PMSELR_EL0); + write_sysreg(val, pmselr_el0); + + /* Save only the stateful writable bits. */ + val =3D __vcpu_sys_reg(vcpu, PMCR_EL0); + mask =3D ARMV8_PMU_PMCR_MASK & + ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C); + write_sysreg(val & mask, pmcr_el0); + + /* + * When handling these: + * 1. Apply only the bits for guest counters (indicated by mask) + * 2. Use the different registers for set and clear + */ + mask =3D kvm_pmu_guest_counter_mask(pmu); + + /* Clear the hardware overflow flags so there is no chance of + * creating spurious interrupts. The hardware here is never + * the canonical version anyway. + */ + write_sysreg(mask, pmovsclr_el0); + + val =3D __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); + write_sysreg(val & mask, pmcntenset_el0); + write_sysreg(~val & mask, pmcntenclr_el0); + + val =3D __vcpu_sys_reg(vcpu, PMINTENSET_EL1); + write_sysreg(val & mask, pmintenset_el1); + write_sysreg(~val & mask, pmintenclr_el1); + + preempt_enable(); +} + +/** + * kvm_pmu_put() - Put untrapped PMU registers + * @vcpu: Pointer to struct kvm_vcpu + * + * Put all untrapped PMU registers from the VCPU into the PCPU. Mask + * to only bits belonging to guest-reserved counters and leave + * host-reserved counters alone in bitmask registers. + */ +void kvm_pmu_put(struct kvm_vcpu *vcpu) +{ + struct arm_pmu *pmu; + unsigned long guest_counters; + u64 mask; + u8 i; + u64 val; + + /* + * If we aren't guest-owned then we know the guest is not + * accessing the PMU anyway, so no need to bother with the + * swap. + */ + if (!kvm_vcpu_pmu_is_partitioned(vcpu)) + return; + + preempt_disable(); + + pmu =3D vcpu->kvm->arch.arm_pmu; + guest_counters =3D kvm_pmu_guest_counter_mask(pmu); + + for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) { + if (i =3D=3D ARMV8_PMU_CYCLE_IDX) { + val =3D read_sysreg(pmccntr_el0); + } else { + write_sysreg(i, pmselr_el0); + val =3D read_sysreg(pmxevcntr_el0); + } + + __vcpu_assign_sys_reg(vcpu, PMEVCNTR0_EL0 + i, val); + } + + val =3D read_sysreg(pmselr_el0); + __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, val); + + val =3D read_sysreg(pmcr_el0); + __vcpu_assign_sys_reg(vcpu, PMCR_EL0, val); + + /* Mask these to only save the guest relevant bits. */ + mask =3D kvm_pmu_guest_counter_mask(pmu); + + val =3D read_sysreg(pmcntenset_el0); + __vcpu_assign_sys_reg(vcpu, PMCNTENSET_EL0, val & mask); + + val =3D read_sysreg(pmintenset_el1); + __vcpu_assign_sys_reg(vcpu, PMINTENSET_EL1, val & mask); + + preempt_enable(); +} diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 6aaeb27642540..fa881dc5f5832 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -96,6 +96,10 @@ bool kvm_pmu_is_partitioned(struct arm_pmu *pmu); =20 u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu); u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu); +u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu); +u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu); +void kvm_pmu_load(struct kvm_vcpu *vcpu); +void kvm_pmu_put(struct kvm_vcpu *vcpu); =20 #if !defined(__KVM_NVHE_HYPERVISOR__) bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu); @@ -167,6 +171,8 @@ static inline u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) { return 0; } +static inline void kvm_pmu_load(struct kvm_vcpu *vcpu) {} +static inline void kvm_pmu_put(struct kvm_vcpu *vcpu) {} static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) {} static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, @@ -269,6 +275,16 @@ static inline bool kvm_pmu_is_partitioned(void *pmu) return false; } =20 +static inline u64 kvm_pmu_host_counter_mask(void *kvm) +{ + return ~0; +} + +static inline u64 kvm_pmu_guest_counter_mask(void *kvm) +{ + return 0; +} + #endif =20 #endif --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C576B40B6E8 for ; Mon, 4 May 2026 21:18:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929530; cv=none; b=Kuir8T56tWMYyVGNmCYaNdw1HuCs8+bZGPndk7IyvUCSwpJfqLRUCUT+P0wdkAv/mplv5G++EA7/KNasrMaKE/MK4uU4vQtMsIEpgQ+bSgek3aUpMmYM/ROJ9nwFZTo41hj0f5EBO2WNhQnYLSI5/EbtxNbzXy0DgA7htUKvXK0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929530; c=relaxed/simple; bh=51x2qNUJZmu8VDSVPga7Ou82hKP9Y1fm/Wq0S6zmqPo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZUc1QV1WTsTTNw5TxBqIxt29QuuO4l+zWqaojFfeQImRLXkj3tx9PELqNYFUqLIyQfPio6fkdPJihSWLjp2G/ve0/HSDoFN9vdWU4fc5Zd/pCKUL48EYFeGp6lf49d3MpbMgOqHb4UrNLqmH0aTIHyE7n4Nd0KK05US5t6eyxk4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ir+/0SHz; arc=none smtp.client-ip=209.85.161.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ir+/0SHz" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-6949742b3ebso9964479eaf.3 for ; Mon, 04 May 2026 14:18:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929523; x=1778534323; darn=vger.kernel.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=ikJw7Yl21sbWiTQZxDvWwtZnXGp+jw2m9ITCB7brm+8=; b=ir+/0SHz4yMowC9EeZO5C8GyDjV/+pijEF6lPJ8laFtVBvRWR+3/+kTu9V5CdwgLJY 5LjB7d8duDMerUeuz+ul9dkchPpG5UOnWzZweX3X9C8yuYSB/rabGOnJmwEcAAKWLd/I Tkkn2bYJHoGNFV5SivZOXsGONk9k5CUoa2eANlaA93GMx7iJI1FeFh7xlTMe5Lbi6Meg a2fKwErhM4W0a8uwTU5pZppBWVYbpYZBmj+YgdTRDLSKFo9Qguikh2tUuRdfrHIQUu9i sBZxBEuSGntnjCOv0TpNu5TR6bFh4t36qZdY/XPO93W67+kkHi9UtZNWEOW50+dhGAAv cXeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929523; x=1778534323; 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=ikJw7Yl21sbWiTQZxDvWwtZnXGp+jw2m9ITCB7brm+8=; b=OAHscQ+2Y/EWmv4gQz2fWBH7dhmMTX3A1gR5f+Kc/B/i0nG1eYAQgROLQUDguXthHQ +DjQX8C/UnA/ainSVAV0411/9W3OkgCdLhbghM/gGxDss1GOVRuqtIBI0GZdhUWqa2g2 aUNzEGu6sdTme2NJ1jzE/296jAHuCEEygloOpCLWVO7IZBVf+XTXbh5M+cXPbZ6lLdgm DVevaoo36GjntlBrOLN9YvqOfh75/F0Az6wLyyU6bgGvSbJcLUvacibweDR7zQzdfqst pR5FdM231B2/WYvx0eFD0iBNk9+PBPlx0UXgt1ITUNF9GbrV5XYFulYLqqkAbk1J+TMJ DdyQ== X-Forwarded-Encrypted: i=1; AFNElJ/QlTlDkX1lI2M6JoptWrleyzMTvhPkpOyx02Z7e/L+QTer2L4zF2n/EPX/fIpsFZKalTamBBADXkvzhVs=@vger.kernel.org X-Gm-Message-State: AOJu0YxFTctPDNrq0pfE35Dlb86OP4BwpAWTU9KS1dlns+RlI2Uzyxkd TSarOB97RkwIK/RkEw48yDWOgD1kdv77jQzixXIo/CuNpt0+L/yfXHgsbv9NIT63CfW2mQAgqFR aS5vUbJR+6p95n6qwPSbtfTfu6A== X-Received: from ilaj29.prod.google.com ([2002:a05:6e02:219d:b0:4fd:6acc:4c7b]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2012:b0:696:1a85:586b with SMTP id 006d021491bc7-69697c3388cmr5842328eaf.35.1777929522601; Mon, 04 May 2026 14:18:42 -0700 (PDT) Date: Mon, 4 May 2026 21:18:04 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-12-coltonlewis@google.com> Subject: [PATCH v7 11/20] KVM: arm64: Enforce PMU event filter at vcpu_load() From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The KVM API for event filtering says that counters do not count when blocked by the event filter. To enforce that, the event filter must be rechecked on every load since it might have changed since the last time the guest wrote a value. If the event is filtered, exclude counting at all exception levels before writing the hardware. Signed-off-by: Colton Lewis --- arch/arm64/kvm/pmu-direct.c | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 360d022d918d5..2252d3b905db9 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -138,6 +138,59 @@ u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu) return 0; } =20 +/** + * kvm_pmu_apply_event_filter() + * @vcpu: Pointer to vcpu struct + * + * To uphold the guarantee of the KVM PMU event filter, we must ensure + * no counter counts if the event is filtered. Accomplish this by + * filtering all exception levels if the event is filtered. + */ +static void kvm_pmu_apply_event_filter(struct kvm_vcpu *vcpu) +{ + struct arm_pmu *pmu =3D vcpu->kvm->arch.arm_pmu; + unsigned long guest_counters; + u64 evtyper_set =3D ARMV8_PMU_EXCLUDE_EL0 | + ARMV8_PMU_EXCLUDE_EL1; + u64 evtyper_clr =3D ARMV8_PMU_INCLUDE_EL2; + bool guest_include_el2; + u8 i; + u64 val; + u64 evsel; + + if (!pmu) + return; + + guest_counters =3D kvm_pmu_guest_counter_mask(pmu); + + for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) { + if (i =3D=3D ARMV8_PMU_CYCLE_IDX) { + val =3D __vcpu_sys_reg(vcpu, PMCCFILTR_EL0); + evsel =3D ARMV8_PMUV3_PERFCTR_CPU_CYCLES; + } else { + val =3D __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + i); + evsel =3D val & kvm_pmu_event_mask(vcpu->kvm); + } + + guest_include_el2 =3D (val & ARMV8_PMU_INCLUDE_EL2); + val &=3D ~evtyper_clr; + + if (unlikely(is_hyp_ctxt(vcpu)) && guest_include_el2) + val &=3D ~ARMV8_PMU_EXCLUDE_EL1; + + if (vcpu->kvm->arch.pmu_filter && + !test_bit(evsel, vcpu->kvm->arch.pmu_filter)) + val |=3D evtyper_set; + + if (i =3D=3D ARMV8_PMU_CYCLE_IDX) { + write_sysreg(val, pmccntr_el0); + } else { + write_sysreg(i, pmselr_el0); + write_sysreg(val, pmxevtyper_el0); + } + } +} + /** * kvm_pmu_load() - Load untrapped PMU registers * @vcpu: Pointer to struct kvm_vcpu @@ -165,6 +218,7 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu) =20 pmu =3D vcpu->kvm->arch.arm_pmu; guest_counters =3D kvm_pmu_guest_counter_mask(pmu); + kvm_pmu_apply_event_filter(vcpu); =20 for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) { val =3D __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i); --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oa1-f74.google.com (mail-oa1-f74.google.com [209.85.160.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0BE28410D19 for ; Mon, 4 May 2026 21:18:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929531; cv=none; b=P6gTA1RP0KKjfnL/RQkWZKF9NB+kdsqL1IzTkeFgBfRNHTDdoSMUbV1ePthoQgw9WpKYcSiJa+Ovanz4steHng4Sg8gkHN3mqkF69CCBBJvgy31Ct774T8EfOnVQLprqe+AYuX6tCl0hxA3VfqDDQ+A4lfopkL4e+g6H5i8NESE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929531; c=relaxed/simple; bh=S0Rh88cIzrwPcgFazDDX9G1MacYZO4t4rc6Ti6y7iLw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JC6jF83slmwei7XGI24DnT0egumPFNKo6/YZgktGsJN2uNJNqDjTduUqkr/chqsqGswo0bhO6yZL6r087VdLqYEjYm37732OUBI/tlqspaCyxEfpq43mMy2ss3CyAXvmZvTP434HchEHNwmt0sM7w26di8n386ZAOQhPXM6Tpg0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=suUKP8VD; arc=none smtp.client-ip=209.85.160.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="suUKP8VD" Received: by mail-oa1-f74.google.com with SMTP id 586e51a60fabf-42fb9d10232so10197466fac.2 for ; Mon, 04 May 2026 14:18:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929524; x=1778534324; darn=vger.kernel.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=IHqwi47gLxulqjFwzyPWRWmJ21m3KfZnivqkYOWLGBw=; b=suUKP8VDI/w6B/+nWuXC9PVT+Uanm7EuOrfR1dptORtN5vfsGXf+LmnkhhqUA0gWA9 5I7rtXK68DW2fC5SxchvRHMHrlEzSotlrvoT+Rlbn8GnadevTw7VNFn3tjRkMwAeqlUv YsawUIYcHYTkn+bs8HElGlx318phzbGts5Y+4fuIfF1kWKKypHvJlz2wR1PRnwgoAqHI DNwMeoeVYSki2i8pnTBjxZ3WgC9gsud8K5lRsXxeEOH7R7K4aXyWQSitV4gLk44uFCYQ fAgLlAdqpLSzzVahrQh6OQyco3b9HLSfvT+mBJzoOIvetV5hRWthWAX/mb1ZoBbCKIac 5sPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929524; x=1778534324; 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=IHqwi47gLxulqjFwzyPWRWmJ21m3KfZnivqkYOWLGBw=; b=MtE8rPkwBNLsdTOQEif8iek7yOSQdiosPKEXCP64iekdE2JcSgzwld2Pg+sqAigtzg 94OnuVmi14owEpz2fBx+ti7a9gAo4+/nMM/Td42xwwddqWBEb6Ez9YZltIGdKnY8/afo /gYY0v4bRwk4DdsyqHnTiOVZGSr5Xm5HfHZOCjyBcJW8ujzsIr/URBMQQa6MuN5qV5Lb w/r9zlYS5OkupciHZU3QsSqzEOjK9/dCuZeKnbSe+fAWMCpitAogRxrY918tMfS5A8sj wCj75GWK26i6/T9A717I9aOykvdCouhHQCN6Wa8+Q5Q/aK2fEelGYvTVh4a0yLDQ4zNN mCgw== X-Forwarded-Encrypted: i=1; AFNElJ9tTAjMNGg1W6eCpvFGiocs0ok7WCyz8Ia8IYKuXcS0uEOupHn/aR3yr8Ca+WYEqHJE9McXwRpJujPPGzk=@vger.kernel.org X-Gm-Message-State: AOJu0Yx9ucPlMZXyE8fD8tQ6c5TpB9aBQ1njVTso/6yxAGdk3ArfwfTj 1DuOteJU3UA7EuqTgec/08vTgG45d2evEuWstVDTaSWK7Si4ZObBbvIDC7oiX7gkgWlOTVI9cwn LHkaEtxUMtCDPU0fTisBfbbT3oA== X-Received: from iods12.prod.google.com ([2002:a05:6602:324c:b0:96b:30a7:3dc9]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:211:b0:696:8278:c619 with SMTP id 006d021491bc7-696979ad7d2mr5858531eaf.6.1777929523888; Mon, 04 May 2026 14:18:43 -0700 (PDT) Date: Mon, 4 May 2026 21:18:05 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-13-coltonlewis@google.com> Subject: [PATCH v7 12/20] perf: Add perf_pmu_resched_update() From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" To modify PMU guest counter reservations dynamically, we need to update the available counters safely. Introduce perf_pmu_resched_update() to allow updating the PMU struct in between scheduling perf events out and scheduling them back in again. It takes a callback operation to call in between schedule out and schedule in. This accomplishes the goal with minimal perf API expansion. Refactor ctx_resched call the callback in the right place. Signed-off-by: Colton Lewis --- include/linux/perf_event.h | 3 +++ kernel/events/core.c | 28 +++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 48d851fbd8ea5..a08db3ee38b10 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1242,6 +1242,9 @@ extern int perf_event_task_disable(void); extern int perf_event_task_enable(void); =20 extern void perf_pmu_resched(struct pmu *pmu); +extern void perf_pmu_resched_update(struct pmu *pmu, + void (*update)(struct pmu *, void *), + void *data); =20 extern int perf_event_refresh(struct perf_event *event, int refresh); extern void perf_event_update_userpage(struct perf_event *event); diff --git a/kernel/events/core.c b/kernel/events/core.c index 89b40e4397177..62fec73caabad 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2983,9 +2983,10 @@ static void perf_event_sched_in(struct perf_cpu_cont= ext *cpuctx, * event_type is a bit mask of the types of events involved. For CPU event= s, * event_type is only either EVENT_PINNED or EVENT_FLEXIBLE. */ -static void ctx_resched(struct perf_cpu_context *cpuctx, - struct perf_event_context *task_ctx, - struct pmu *pmu, enum event_type_t event_type) +static void __ctx_resched(struct perf_cpu_context *cpuctx, + struct perf_event_context *task_ctx, + struct pmu *pmu, enum event_type_t event_type, + void (*update)(struct pmu *, void *), void *data) { bool cpu_event =3D !!(event_type & EVENT_CPU); struct perf_event_pmu_context *epc; @@ -3021,6 +3022,9 @@ static void ctx_resched(struct perf_cpu_context *cpuc= tx, else if (event_type & EVENT_PINNED) ctx_sched_out(&cpuctx->ctx, pmu, EVENT_FLEXIBLE); =20 + if (update) + update(pmu, data); + perf_event_sched_in(cpuctx, task_ctx, pmu, 0); =20 for_each_epc(epc, &cpuctx->ctx, pmu, 0) @@ -3032,6 +3036,24 @@ static void ctx_resched(struct perf_cpu_context *cpu= ctx, } } =20 +static void ctx_resched(struct perf_cpu_context *cpuctx, + struct perf_event_context *task_ctx, + struct pmu *pmu, enum event_type_t event_type) +{ + __ctx_resched(cpuctx, task_ctx, pmu, event_type, NULL, NULL); +} + +void perf_pmu_resched_update(struct pmu *pmu, void (*update)(struct pmu *,= void *), void *data) +{ + struct perf_cpu_context *cpuctx =3D this_cpu_ptr(&perf_cpu_context); + struct perf_event_context *task_ctx =3D cpuctx->task_ctx; + + perf_ctx_lock(cpuctx, task_ctx); + __ctx_resched(cpuctx, task_ctx, pmu, EVENT_ALL|EVENT_CPU, update, data); + perf_ctx_unlock(cpuctx, task_ctx); +} +EXPORT_SYMBOL_GPL(perf_pmu_resched_update); + void perf_pmu_resched(struct pmu *pmu) { struct perf_cpu_context *cpuctx =3D this_cpu_ptr(&perf_cpu_context); --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f73.google.com (mail-ot1-f73.google.com [209.85.210.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8181F346E60 for ; Mon, 4 May 2026 21:18:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929534; cv=none; b=RqgCMw9WE4C4F+qW4DY23/xO/Z8P81tq2+6z6PtIfMaIkoOlqXLehOCbPACeuvapuXOI5zQnpLa3A23GZ2M1SRRlppqXbuOTPLjn58IN07NzkBdYe5DnWCrhOj2j4WSYNZDyDh8IxXf0ENflqvn4GcxHnK2XKcEcLoo3otQCWgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929534; c=relaxed/simple; bh=fWZg9y5aLAFSBW2MiwOhHFnYu+Ty84504Z5PuiRS96o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=de7cS89h8ww7+SLV9a66h18k3lNBtnNO8i6dJS0Ppy6+7uq2CTV4hl15tWnzIOgwRywkjV26DIb44gaWub7HbY8NPyFM64ejE7iY+TyNYLILswGZp7Ava7tRxnu+yuZBsOU0F+8eUPOhlPSnWkGDmiccQQ5Z+D8Fdt4aKSJXOoU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=I7+RVT59; arc=none smtp.client-ip=209.85.210.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="I7+RVT59" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7dcd603855aso9860923a34.0 for ; Mon, 04 May 2026 14:18:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929525; x=1778534325; darn=vger.kernel.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=vvvExWFpSdxF1U5r97vObpZLpBpbjqEmnZt44hIldRo=; b=I7+RVT594HzAjLodptZF7ldu0HiqJ25zvNcbD51VDkRdkubXJjQJohPbAmtyjlr6wS 2do8Pgx5lU0wGmeG7gWViK0l3Q7nC0TZ7g2dBTkVrl5X89JAnMPxp/WXYbziCXPLVQ7n sRO/UlUuKaKfk57DVw0yV/TAPvRMuLZKLP50Afbghhb0ZLYSoOL4FC9sENi+7OTBISJ6 Y7RDmLXLz4wU6ZMSLXfsxWBQr9tHoRq2T/YQMS9aw8otlFLengTBcAESp05TaMhGlILc qUn0rk19sODgNrQivabAeSAkz2FHI/7kzqVjuOUFMWJ3RxbLZcL7eem02wWnxdzLHqu4 ZpMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929525; x=1778534325; 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=vvvExWFpSdxF1U5r97vObpZLpBpbjqEmnZt44hIldRo=; b=ANnE4Z0zbjJAO+4ICvBJ7Slo3ktvkjiRYVJRYrtxm9aYI0IkIYA1/ootQ9r7aj8JBw fwSRP2W3Ih6a6qwA/1aDRyIx1F558/diqT6bq6eAvinOq/fHVFEnI/IR0CdZibq3srCJ 9bSdwPfrymsq+dlPiRKWFwWGaLCzSnqfnPYpebWckFRmKhLSKBXhkWJMzzTmOKm0cuRn C9eIGoPeG/3oCPfwG4ZoW0qbLUjb12VNjr3R5Qaqon7zk2+/FEMDd+oFqs+/53TXu2hu bjaIT3AbjSKInQYmlskn/QnIlxqvquoPEJagwzf3keEegI92Dl4Ky3zIy1EWJsyDwA1T 7FUw== X-Forwarded-Encrypted: i=1; AFNElJ+NEpBM3CWl/MwRW4k7kjVXo1HO+dsNn2QRV02LuHQ5gaTEdgXfw+ZCBTLcZbhMGWVfjDLyYEfAW0S5JQU=@vger.kernel.org X-Gm-Message-State: AOJu0Ywncn71pTK4wYsg80Gka1UMfOM59XynByFY3rfGyaARRPhsfmVo E5uSPkEibS73bIUtwoh7T2GOFPQvsmSVsCIt7xQS3JZ8EUEkJKvX94Wn0JR9Ts+TziCeBf7/lhT nbttZ3TUvmNTk01nEZFaf8PJkxg== X-Received: from ilpy14.prod.google.com ([2002:a92:c74e:0:b0:4fe:8f5f:a030]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2b03:b0:693:7a6f:b311 with SMTP id 006d021491bc7-6989150d84emr387036eaf.22.1777929525215; Mon, 04 May 2026 14:18:45 -0700 (PDT) Date: Mon, 4 May 2026 21:18:06 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-14-coltonlewis@google.com> Subject: [PATCH v7 13/20] KVM: arm64: Apply dynamic guest counter reservations From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Apply dynamic guest counter reservations by checking if the requested guest mask collides with any events the host has scheduled and calling pmu_perf_resched_update() with a hook that updates the mask of available counters in between schedule out and schedule in. Signed-off-by: Colton Lewis --- arch/arm64/kvm/pmu-direct.c | 69 ++++++++++++++++++++++++++++++++++++ include/linux/perf/arm_pmu.h | 1 + 2 files changed, 70 insertions(+) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 2252d3b905db9..14cc419dbafad 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -100,6 +100,73 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) return *host_data_ptr(nr_event_counters); } =20 +/* Callback to update counter mask between perf scheduling */ +static void kvm_pmu_update_mask(struct pmu *pmu, void *data) +{ + struct arm_pmu *arm_pmu =3D to_arm_pmu(pmu); + unsigned long *new_mask =3D data; + + bitmap_copy(arm_pmu->cntr_mask, new_mask, ARMPMU_MAX_HWEVENTS); +} + +/** + * kvm_pmu_set_guest_counters() - Handle dynamic counter reservations + * @cpu_pmu: struct arm_pmu to potentially modify + * @guest_mask: new guest mask for the pmu + * + * Check if guest counters will interfere with current host events and + * call into perf_pmu_resched_update if a reschedule is required. + */ +static void kvm_pmu_set_guest_counters(struct arm_pmu *cpu_pmu, u64 guest_= mask) +{ + struct pmu_hw_events *cpuc =3D this_cpu_ptr(cpu_pmu->hw_events); + DECLARE_BITMAP(guest_bitmap, ARMPMU_MAX_HWEVENTS); + DECLARE_BITMAP(new_mask, ARMPMU_MAX_HWEVENTS); + bool need_resched =3D false; + + bitmap_from_arr64(guest_bitmap, &guest_mask, ARMPMU_MAX_HWEVENTS); + bitmap_copy(new_mask, cpu_pmu->hw_cntr_mask, ARMPMU_MAX_HWEVENTS); + + if (guest_mask) { + /* Subtract guest counters from available host mask */ + bitmap_andnot(new_mask, new_mask, guest_bitmap, ARMPMU_MAX_HWEVENTS); + + /* Did we collide with an active host event? */ + if (bitmap_intersects(cpuc->used_mask, guest_bitmap, ARMPMU_MAX_HWEVENTS= )) { + int idx; + + need_resched =3D true; + cpuc->host_squeezed =3D true; + + /* Look for pinned events that are about to be preempted */ + for_each_set_bit(idx, guest_bitmap, ARMPMU_MAX_HWEVENTS) { + if (test_bit(idx, cpuc->used_mask) && cpuc->events[idx] && + cpuc->events[idx]->attr.pinned) { + pr_warn_ratelimited("perf: Pinned host event squeezed out by KVM gues= t PMU partition\n"); + break; + } + } + } + } else { + /* + * Restoring to hw_cntr_mask. + * Only resched if we previously squeezed an event. + */ + if (cpuc->host_squeezed) { + need_resched =3D true; + cpuc->host_squeezed =3D false; + } + } + + if (need_resched) { + /* Collision: run full perf reschedule */ + perf_pmu_resched_update(&cpu_pmu->pmu, kvm_pmu_update_mask, new_mask); + } else { + /* Host was never using guest counters anyway */ + bitmap_copy(cpu_pmu->cntr_mask, new_mask, ARMPMU_MAX_HWEVENTS); + } +} + /** * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved counters * @pmu: Pointer to arm_pmu struct @@ -218,6 +285,7 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu) =20 pmu =3D vcpu->kvm->arch.arm_pmu; guest_counters =3D kvm_pmu_guest_counter_mask(pmu); + kvm_pmu_set_guest_counters(pmu, guest_counters); kvm_pmu_apply_event_filter(vcpu); =20 for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) { @@ -319,5 +387,6 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) val =3D read_sysreg(pmintenset_el1); __vcpu_assign_sys_reg(vcpu, PMINTENSET_EL1, val & mask); =20 + kvm_pmu_set_guest_counters(pmu, 0); preempt_enable(); } diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index f7b000bb3eca8..63f88fec5e80f 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -75,6 +75,7 @@ struct pmu_hw_events { =20 /* Active events requesting branch records */ unsigned int branch_users; + bool host_squeezed; }; =20 enum armpmu_attr_groups { --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oa1-f74.google.com (mail-oa1-f74.google.com [209.85.160.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB3E2421F0E for ; Mon, 4 May 2026 21:18:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929536; cv=none; b=nIdCYOOCp5e2GVN66yrpvByogUhjxN0zVbhb+hkv4PjZSuoUZNE6nnF8vuhz25VGttzv3Ikf+FItaiC0ywL3xj2JeXiVzMDhDSgwE9OETLoBaC6NMsRs6APWfgmUhziHWFtBF7LF2x/n7lhbiOXVTgi+fVUMgXi0tJYlyq+veDI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929536; c=relaxed/simple; bh=JDqmu+DnhXRfNiWmxN2ANXkk71WPrasilO+JqIo57UQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=h+8o6pzY/m6IpVXwTkEpUcRBehXKNgfJJg2mjFs+x5MwAe2Y3XVMxJPnpSAQIRcwRf7ee3qTa/Y3fcoif1W0iGEWMLRYRQyIKIK2bNMCfLmv/lz265mofcEtOxW/prkKROLn+u0XM0Sd0CvbLvIZtKj5won0MXJN2RkRejuYyvw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=EC+VZqzK; arc=none smtp.client-ip=209.85.160.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="EC+VZqzK" Received: by mail-oa1-f74.google.com with SMTP id 586e51a60fabf-42fb0f12b10so5334250fac.2 for ; Mon, 04 May 2026 14:18:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929527; x=1778534327; darn=vger.kernel.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=IBRwxe29L12LD2xEcP8UBXi5vcKDWFkDhBR645b//Pw=; b=EC+VZqzKavRnal/97stnhkoGLnB6Fj6svott/IEuUnNCzIedMaolUqgifvTkGcLwm8 UDvx0dtJ7NlWk33R3df6WDk4pD3dRLmjdCIJdRc4rLSqoaruABoXhj9zGnO9a/X7ScH7 3KHdPrbyGcSy0TYiOHPRL/goZuXAug3xaFWpMi9SrcaoAQIpTA2GywO3bS+gzmsiCpIv gl86CDpUazN9BmXTSKVG5vEASIuk85nCgTuj5DQGdZhKGrraKiMi6Edwzef9Ube/6mQb 9+7rMvLU7u0mxV4KraY5WN+W6FZB0w05pZkYALHlM6hU575hQqqeSMgtAM6tiVm6G9jS Vl2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929527; x=1778534327; 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=IBRwxe29L12LD2xEcP8UBXi5vcKDWFkDhBR645b//Pw=; b=k96nOaH3JpoQawIjlA5rTvUbfsG7pZrrrP+g3sbXIGqiXrcdM/+/ecDPClmqqMYPAz 6xgT91W4TYYeYMLyR7lFk9ogFuwUC9Msjrpbo1nGEturFW/a10gE4FE/LmDQwwpWsIQc sPMcne++K5f7tl+2ShqmPyxXSrdC3iUsoWDFaE5ApkKOzYHQu4W7WypOIO7oBFiqWlrR afhBP2cLdqUnk2R+R6cexHphGez+QY9VyBNsWRJMoBiG5R7M5cG9yRZaxdj88yGHCuPP gY1YNtSR6HjdsSKAfH/MV2BI87HNUvTWnCcvwdscNPyRGeFdMVK5H6k8m+vW+dspBEnF elBQ== X-Forwarded-Encrypted: i=1; AFNElJ+ySCLUS7EvRBSHrLstRDTDAL+i/sNzZhPQEVND51C7Js1XJqKiOPcatnHt1SQsaDZINmBORTCJ2BCKhzk=@vger.kernel.org X-Gm-Message-State: AOJu0YzsGhoyUmYo1A/Spgwt5S+AhnVMvRcrQ9MwMXPCzzhGGTnaCMtm YyT5hRtYvoX5qTMlSZ7nFxMvjeXRrRHroDHN1pIkYG77KyGtnw8Uz3QC5Y3mKIXhBMtHII448WA wO5WRoZuQfbN+ospmb1OBCdWRzA== X-Received: from iljy18.prod.google.com ([2002:a05:6e02:f52:b0:4fe:8f6f:9544]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:178f:b0:696:77e2:a78 with SMTP id 006d021491bc7-6998a5aaa71mr161406eaf.23.1777929526446; Mon, 04 May 2026 14:18:46 -0700 (PDT) Date: Mon, 4 May 2026 21:18:07 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-15-coltonlewis@google.com> Subject: [PATCH v7 14/20] KVM: arm64: Implement lazy PMU context swaps From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Since many guests will never touch the PMU, they need not pay the cost of context swapping those registers. Use an enum to implement a simple state machine for PMU register access. The PMU is either free or guest owned. We only need to context swap if the PMU registers are guest owned. The PMU initially starts as free and only transitions to guest owned if a guest has touched the PMU registers. Signed-off-by: Colton Lewis --- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/asm/kvm_types.h | 6 +++++- arch/arm64/kvm/debug.c | 2 +- arch/arm64/kvm/pmu-direct.c | 25 +++++++++++++++++++++++-- arch/arm64/kvm/sys_regs.c | 11 +++++++++++ include/kvm/arm_pmu.h | 5 +++++ 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 1f789ba589d56..891433fe304ac 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1404,6 +1404,7 @@ static inline bool kvm_system_needs_idmapped_vectors(= void) return cpus_have_final_cap(ARM64_SPECTRE_V3A); } =20 +void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu); void kvm_init_host_debug_data(void); void kvm_debug_init_vhe(void); void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/include/asm/kvm_types.h b/arch/arm64/include/asm/kv= m_types.h index 9a126b9e2d7c9..4e39cbc80aa0b 100644 --- a/arch/arm64/include/asm/kvm_types.h +++ b/arch/arm64/include/asm/kvm_types.h @@ -4,5 +4,9 @@ =20 #define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 40 =20 -#endif /* _ASM_ARM64_KVM_TYPES_H */ +enum vcpu_pmu_register_access { + VCPU_PMU_ACCESS_FREE, + VCPU_PMU_ACCESS_GUEST_OWNED, +}; =20 +#endif /* _ASM_ARM64_KVM_TYPES_H */ diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index 0ab89c91e19cb..c2cf6b308ec60 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -34,7 +34,7 @@ static int cpu_has_spe(u64 dfr0) * - Self-hosted Trace Filter controls (MDCR_EL2_TTRF) * - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB) */ -static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) +void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) { int hpmn =3D kvm_pmu_hpmn(vcpu); =20 diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 14cc419dbafad..881cea5117515 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -72,10 +72,29 @@ bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu) u8 hpmn =3D vcpu->kvm->arch.nr_pmu_counters; =20 return kvm_vcpu_pmu_is_partitioned(vcpu) && + vcpu->arch.pmu.access =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED && cpus_have_final_cap(ARM64_HAS_FGT) && (hpmn !=3D 0 || cpus_have_final_cap(ARM64_HAS_HPMN0)); } =20 +/** + * kvm_pmu_set_guest_owned() + * @vcpu: Pointer to vcpu struct + * + * Reconfigure the guest for physical access of PMU hardware if + * allowed. This means reconfiguring mdcr_el2 and loading the vCPU + * state onto hardware. + * + */ +void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_pmu_is_partitioned(vcpu) && + vcpu->arch.pmu.access =3D=3D VCPU_PMU_ACCESS_FREE) { + vcpu->arch.pmu.access =3D VCPU_PMU_ACCESS_GUEST_OWNED; + kvm_arm_setup_mdcr_el2(vcpu); + } +} + /** * kvm_pmu_hpmn() - Calculate HPMN field value * @vcpu: Pointer to struct kvm_vcpu @@ -278,7 +297,8 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu) * If we aren't guest-owned then we know the guest isn't using * the PMU anyway, so no need to bother with the swap. */ - if (!kvm_vcpu_pmu_is_partitioned(vcpu)) + if (!kvm_vcpu_pmu_is_partitioned(vcpu) || + vcpu->arch.pmu.access !=3D VCPU_PMU_ACCESS_GUEST_OWNED) return; =20 preempt_disable(); @@ -353,7 +373,8 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) * accessing the PMU anyway, so no need to bother with the * swap. */ - if (!kvm_vcpu_pmu_is_partitioned(vcpu)) + if (!kvm_vcpu_pmu_is_partitioned(vcpu) || + vcpu->arch.pmu.access !=3D VCPU_PMU_ACCESS_GUEST_OWNED) return; =20 preempt_disable(); diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index cc3d1804ab200..e4840d93a769f 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1035,6 +1035,7 @@ static bool pmu_access_event_counter_el0_disabled(str= uct kvm_vcpu *vcpu) static void pmu_write_pmcr(struct kvm_vcpu *vcpu, u64 val) { if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_guest_owned(vcpu); write_sysreg(val, pmcr_el0); return; } @@ -1086,6 +1087,7 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct= sys_reg_params *p, static void pmu_write_pmselr(struct kvm_vcpu *vcpu, u64 val) { if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_guest_owned(vcpu); write_sysreg(val, pmselr_el0); return; } @@ -1193,6 +1195,8 @@ static void pmu_write_evcntr(struct kvm_vcpu *vcpu, u= 64 val, u64 idx) return; } =20 + kvm_pmu_set_guest_owned(vcpu); + if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) { write_sysreg(val, pmccntr_el0); return; @@ -1281,6 +1285,7 @@ static void pmu_write_evtyper(struct kvm_vcpu *vcpu, = u64 val, u64 idx) u64 mask; =20 if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_guest_owned(vcpu); mask =3D kvm_pmu_evtyper_mask(vcpu->kvm); __vcpu_assign_sys_reg(vcpu, PMEVTYPER0_EL0 + idx, val & mask); return; @@ -1350,6 +1355,8 @@ static int get_pmreg(struct kvm_vcpu *vcpu, const str= uct sys_reg_desc *r, u64 *v static void pmu_write_pmcnten(struct kvm_vcpu *vcpu, u64 val, bool set) { if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_guest_owned(vcpu); + if (set) write_sysreg(val, pmcntenset_el0); else @@ -1398,6 +1405,8 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, str= uct sys_reg_params *p, static void pmu_write_pminten(struct kvm_vcpu *vcpu, u64 val, bool set) { if (kvm_vcpu_pmu_is_partitioned(vcpu)) { + kvm_pmu_set_guest_owned(vcpu); + if (set) write_sysreg(val, pmintenset_el1); else @@ -1453,6 +1462,8 @@ static bool access_pmovs(struct kvm_vcpu *vcpu, struc= t sys_reg_params *p, return false; =20 if (p->is_write) { + kvm_pmu_set_guest_owned(vcpu); + if (r->CRm & 0x2) /* accessing PMOVSSET_EL0 */ __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, (p->regval & mask)); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index fa881dc5f5832..0de63cc48fef9 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -7,6 +7,7 @@ #ifndef __ASM_ARM_KVM_PMU_H #define __ASM_ARM_KVM_PMU_H =20 +#include #include #include #include @@ -40,6 +41,7 @@ struct kvm_pmu { int irq_num; bool created; bool irq_level; + enum vcpu_pmu_register_access access; }; =20 struct arm_pmu_entry { @@ -101,6 +103,8 @@ u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu); void kvm_pmu_load(struct kvm_vcpu *vcpu); void kvm_pmu_put(struct kvm_vcpu *vcpu); =20 +void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu); + #if !defined(__KVM_NVHE_HYPERVISOR__) bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu); bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu); @@ -173,6 +177,7 @@ static inline u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu) } static inline void kvm_pmu_load(struct kvm_vcpu *vcpu) {} static inline void kvm_pmu_put(struct kvm_vcpu *vcpu) {} +static inline void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu) {} static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) {} static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oi1-f201.google.com (mail-oi1-f201.google.com [209.85.167.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63858423178 for ; Mon, 4 May 2026 21:18:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929536; cv=none; b=qlVsMFPoThtuWIL6/QzWR2kEmfNc8hK+f77UvHvpCGgvCF4WIMhE1UXOcX8j8Sn7Vz+lSlabW5ymSQamZcZr262RURQMplCnbq3vSNesrid2L3SOL/Cn1ITYpv5r/YopXeRQYjoNUHG7kT3KOME0shwkQwW9H6uR/GwDGZhxYRg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929536; c=relaxed/simple; bh=k8iyj1/1BXo34cJSPgEwIM1AOUBCe1L4nQxz3yETR6c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OPRZWSyBWRxZpSCidkcmjaq/L5+IRsvWUS/izm+Tr94OnLG1Wa1WCFQUF+v6Els3mgNEI8pFridwX9BNsIzueM0QbJh96NwhM0qi2olkjTFfzRw6tFQCPf1h7BB6Cq+YqccLuxwWHDucZhgRvdlv+OyRxIVmYzZ4B7tXHuyaRU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=icZP8wti; arc=none smtp.client-ip=209.85.167.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="icZP8wti" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-467e226743eso6887435b6e.0 for ; Mon, 04 May 2026 14:18:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929528; x=1778534328; darn=vger.kernel.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=BCrq3V77MhQdmPRoJ9FxVuhSDGit6Nv3jKM6DslS2rc=; b=icZP8wti2/th4eFCRRVy1SjqOagkqqFmBfMlmjBwLp21plfGbUr7sUyCDsPfzh9Xlc T43zyOhTVARmDzuGvLsrMXaRxAMl9Fc5jwdTW1sdE6502BcTCoQUu1wygw3wSm+thL1h 11AS4UNylrYOqQ8yZAFI+0GKlP3+2dVSHf6S18KkcgHa8kDRCMZ53TuHHMxVaAFgogXL SDmAHgPwahbSUmAc16WD39WlYR3FTnqDicRBpML1VGiPa6B79xthFsOqekradUhvNezZ ShELi+AzCXvt6SMJ9/ZHulhmSJ8XQq3h9w3Q5zKk8wWu8/SMcMNU7N/X+GhdSLG0jfD0 llkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929528; x=1778534328; 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=BCrq3V77MhQdmPRoJ9FxVuhSDGit6Nv3jKM6DslS2rc=; b=Lb1ScH0OXFFv+2nB6NGL4Q1Qy7pcD0yaMEl5+KIoFXFd7LNwpFg6HeNH3PHApWgcja C5cEPD82qUK1FHvtFcBwDYQY3QeiMDNF68y2YIwhncSNEboez2WEFvqtZwqSw5NDIvkE bi+RJdRpseOtypfmG9rncVD/B2bSrtdKY9JWPkJlxFQ6a2fTL6MJBgfkQTlDU83bH1iA ERkn5yrAkcZP4NtbT8sPYP6fdB4FRSUbd5Eglh/h9iUXqYx2qPfFi+8Fu+tuppSjQ1cV f6XM8sPNSUhTGZ/KHkthIt6qVPij0DXC6a4gwk4ewt4WvCCEtNSZNtXET8ds24F7qL/K 4pnA== X-Forwarded-Encrypted: i=1; AFNElJ9zGqwgk4wCR5eEKRfbXwjLrLsNz741udMZLrXQBGWERfRYKWui/D1aFaC5h8g0N41yoRwxlCvt4hwR2bU=@vger.kernel.org X-Gm-Message-State: AOJu0YzOY2ys/tke+7bdC1/j4Im15J8cq426WvtGK9Xp9z6IwIwt5caB PTwZLchGXHnzpyhswd7OF1gYixioBgcgasnIZQfCHDUtok+BlZt5UsAZtWOVPyfpZNqJDh/lycd Pa3Nz5dat1MhbIcX0WrhvsDGLvQ== X-Received: from ilvd7.prod.google.com ([2002:a05:6e02:2147:b0:4fc:3ae9:bfd8]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:6ac3:b0:694:8cb0:4ef8 with SMTP id 006d021491bc7-696979ad636mr5118080eaf.9.1777929527969; Mon, 04 May 2026 14:18:47 -0700 (PDT) Date: Mon, 4 May 2026 21:18:08 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-16-coltonlewis@google.com> Subject: [PATCH v7 15/20] perf: arm_pmuv3: Handle IRQs for Partitioned PMU guest counters From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Because ARM hardware is not yet capable of direct PPI injection into guests, guest counters will still trigger interrupts that need to be handled by the host PMU interrupt handler. Clear the overflow flags in hardware to handle the interrupt as normal, but the virtual overflow register for later injecting the interrupt into the guest. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 6 ++++++ arch/arm64/include/asm/arm_pmuv3.h | 5 +++++ arch/arm64/kvm/pmu-direct.c | 22 ++++++++++++++++++++++ drivers/perf/arm_pmuv3.c | 24 +++++++++++++++++------- include/kvm/arm_pmu.h | 3 +++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index eebc89bdab7a1..0d01508c5b77f 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -180,6 +180,11 @@ static inline void write_pmintenset(u32 val) write_sysreg(val, PMINTENSET); } =20 +static inline u32 read_pmintenset(void) +{ + return read_sysreg(PMINTENSET); +} + static inline void write_pmintenclr(u32 val) { write_sysreg(val, PMINTENCLR); @@ -239,6 +244,7 @@ static inline u64 kvm_pmu_host_counter_mask(struct arm_= pmu *pmu) { return ~0; } +static inline void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovs= r) {} =20 /* PMU Version in DFR Register */ #define ARMV8_PMU_DFR_VER_NI 0 diff --git a/arch/arm64/include/asm/arm_pmuv3.h b/arch/arm64/include/asm/ar= m_pmuv3.h index 27c4d6d47da31..69ff4d014bf39 100644 --- a/arch/arm64/include/asm/arm_pmuv3.h +++ b/arch/arm64/include/asm/arm_pmuv3.h @@ -110,6 +110,11 @@ static inline void write_pmintenset(u64 val) write_sysreg(val, pmintenset_el1); } =20 +static inline u64 read_pmintenset(void) +{ + return read_sysreg(pmintenset_el1); +} + static inline void write_pmintenclr(u64 val) { write_sysreg(val, pmintenclr_el1); diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 881cea5117515..535b4c492ff80 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -411,3 +411,25 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) kvm_pmu_set_guest_counters(pmu, 0); preempt_enable(); } + +/** + * kvm_pmu_handle_guest_irq() - Record IRQs in guest counters + * @pmu: PMU to check for overflows + * @pmovsr: Overflow flags reported by driver + * + * Set overflow flags in guest-reserved counters in the VCPU register + * for the guest to clear later. + */ +void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovsr) +{ + struct kvm_vcpu *vcpu =3D kvm_get_running_vcpu(); + u64 mask =3D kvm_pmu_guest_counter_mask(pmu); + u64 govf =3D pmovsr & mask; + + write_pmovsclr(govf); + + if (!vcpu) + return; + + __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, govf); +} diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 6e447227d801f..16e3700dca645 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -774,16 +774,15 @@ static void armv8pmu_disable_event_irq(struct perf_ev= ent *event) armv8pmu_disable_intens(BIT(event->hw.idx)); } =20 -static u64 armv8pmu_getreset_flags(void) +static u64 armv8pmu_getovf_flags(void) { u64 value; =20 /* Read */ value =3D read_pmovsclr(); =20 - /* Write to clear flags */ - value &=3D ARMV8_PMU_CNT_MASK_ALL; - write_pmovsclr(value); + /* Only report interrupt enabled counters. */ + value &=3D read_pmintenset(); =20 return value; } @@ -897,16 +896,17 @@ static void read_branch_records(struct pmu_hw_events = *cpuc, =20 static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu) { - u64 pmovsr; struct perf_sample_data data; struct pmu_hw_events *cpuc =3D this_cpu_ptr(cpu_pmu->hw_events); struct pt_regs *regs; + u64 host_set =3D kvm_pmu_host_counter_mask(cpu_pmu); + u64 pmovsr; int idx; =20 /* - * Get and reset the IRQ flags + * Get the IRQ flags */ - pmovsr =3D armv8pmu_getreset_flags(); + pmovsr =3D armv8pmu_getovf_flags(); =20 /* * Did an overflow occur? @@ -914,6 +914,12 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu = *cpu_pmu) if (!armv8pmu_has_overflowed(pmovsr)) return IRQ_NONE; =20 + /* + * Guest flag reset is handled the kvm hook at the bottom of + * this function. + */ + write_pmovsclr(pmovsr & host_set); + /* * Handle the counter(s) overflow(s) */ @@ -955,6 +961,10 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu = *cpu_pmu) */ perf_event_overflow(event, &data, regs); } + + if (kvm_pmu_is_partitioned(cpu_pmu)) + kvm_pmu_handle_guest_irq(cpu_pmu, pmovsr); + armv8pmu_start(cpu_pmu); =20 return IRQ_HANDLED; diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 0de63cc48fef9..de058a5347d18 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -95,6 +95,7 @@ void kvm_vcpu_pmu_resync_el0(void); (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3)) =20 bool kvm_pmu_is_partitioned(struct arm_pmu *pmu); +void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovsr); =20 u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu); u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu); @@ -290,6 +291,8 @@ static inline u64 kvm_pmu_guest_counter_mask(void *kvm) return 0; } =20 +static inline void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovs= r) {} + #endif =20 #endif --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f74.google.com (mail-ot1-f74.google.com [209.85.210.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C5D5413249 for ; Mon, 4 May 2026 21:18:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929538; cv=none; b=OtgI7W6Z+M7fpC0g0OIvPKH8I1qw85xJg380wbRqzf9YUFeE7kotZx8vysrhd2TaJ2OLT5TeZC/7G3h8zO4RR3Wypb+rlddAOMeu2Uw4y3CDumWLImiGthvncwfVxgbAHlx3SfRmRXlr8S5LgDpChQUdZankgvlRK0BcHIzP8yo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929538; c=relaxed/simple; bh=Pg+yMKRnt3P95Z9SLp911SYb+lsalrBcQHJk+/HhrK8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KftanOoODdDlMA0uqkHPnSwRDEPLEDtMm6eWRY/fNhL/oMC6yE4t89PDae+lGoeBlFdURcqw+WvUdMVu+8ipkaQqPAEX96T4ROHXURP50JUFtxKYP03QeLL5VROPhqNTw4O7jLNwm/xXCZBcWxtLcW4FJ/4OYUkv9l0geWBJkU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=EKLMdcZd; arc=none smtp.client-ip=209.85.210.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="EKLMdcZd" Received: by mail-ot1-f74.google.com with SMTP id 46e09a7af769-7e0632c733aso2118119a34.1 for ; Mon, 04 May 2026 14:18:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929529; x=1778534329; darn=vger.kernel.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=4eEXyvRG46atk1NeuxPuyTaL3r+WKmiAuf7G/PMx4+I=; b=EKLMdcZdLtlwmqzjgzN+QZf3zDniAwF1wYs6LIdklSQCzIqjiGbkwRKRk+y4g46NoE nzKHaxB6FJcQ8rrGPYTXcET8x5pV9+1w2wb10itZS06qWjCMBerzrrv4MclfX1znr8ow ZSOvywYgHvZUDdHybGXz0Ey/gs/U3SZAbCkxGuEtawpjgxirKX2KrfERVieSYehCHH+A bgRHEVeBXpjkUan3T5giIW04t5lotMJyWDH8HLzKY0jOCK0tLtlLReUaf7pyF7C1dKM9 38rLdACDIK+sqbb74cCEctH8n78eWjSrpudRlENftrczseTATUZCxnjwK/M5CNwv5tDt 7VXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929529; x=1778534329; 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=4eEXyvRG46atk1NeuxPuyTaL3r+WKmiAuf7G/PMx4+I=; b=KtE/4UFCIAbmVq0vyBD9lmaMpFXSckodU586+LIpjSOEFrHdAzg+OyjRZ3KrUkrqr/ QbTd2ZRS/RmKmVSHG9aybRPlbFnxCBMcv9Sgv3gWoTZ7S5KTnhNuWZEnEAt6u76Yat0Y gwmYo8iV8P/5xvkXk1CehnDJ3zrEC/7sHL6HatHHgGcTmq9jiCt0f4vCgGR1cEZB/gjL hLODmiXMGy2Srw54Z2dPZXuZPH0R5UaDbNllR65DdD2Psz/3VdaOro6HGW7joJuCRe9Q dE++03RCRQYK5YWWFxgr0qXsZHu7gggNS6XY74iMa0jjCk5fzj8MAfPJSPGdOO1LT50r n0iA== X-Forwarded-Encrypted: i=1; AFNElJ9aVss4n9N0Ojnv5vxHdx4g6ZRl69EuSfHkkLRGcu/JQ5qdlKUFfqFpr1jyazmxTde1AYzJk/ALG/jCxxU=@vger.kernel.org X-Gm-Message-State: AOJu0Yzl+DolnCw7LCAqzs3G5FereYDRERNYV9374WIMCuVGboGGSjw0 21mBephhxZ8jHMcmMHxsF8JaclS1ipPDCEuCeq6RTM3zaW6YapMTvg5GFTHuy+7TRIzCeXjrOEo vfxDZdA/8lXZqstspF9SzZdF/GQ== X-Received: from ilmv4.prod.google.com ([2002:a92:c6c4:0:b0:4fe:8f7d:8d00]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:20c:b0:696:1f6b:b3b0 with SMTP id 006d021491bc7-69697c50c9dmr5805746eaf.33.1777929529289; Mon, 04 May 2026 14:18:49 -0700 (PDT) Date: Mon, 4 May 2026 21:18:09 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-17-coltonlewis@google.com> Subject: [PATCH v7 16/20] KVM: arm64: Detect overflows for the Partitioned PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When we re-enter the VM after handling a PMU interrupt, calculate whether it was any of the guest counters that overflowed and inject an interrupt into the guest if so. Signed-off-by: Colton Lewis --- arch/arm64/kvm/pmu-direct.c | 30 ++++++++++++++++++++++++++++++ arch/arm64/kvm/pmu-emul.c | 4 ++-- arch/arm64/kvm/pmu.c | 6 +++++- include/kvm/arm_pmu.h | 2 ++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 535b4c492ff80..9693d9eb69daa 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -433,3 +433,33 @@ void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64= pmovsr) =20 __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, govf); } + +/** + * kvm_pmu_part_overflow_status() - Determine if any guest counters have o= verflowed + * @vcpu: Pointer to struct kvm_vcpu + * + * Determine if any guest counters have overflowed and therefore an + * IRQ needs to be injected into the guest. If access is still free, + * then the guest hasn't accessed the PMU yet so we know the guest + * context is not loaded onto the pCPU and an overflow is impossible. + * + * Return: True if there was an overflow, false otherwise + */ +bool kvm_pmu_part_overflow_status(struct kvm_vcpu *vcpu) +{ + struct arm_pmu *pmu; + u64 mask, pmovs, pmint, pmcr; + bool overflow; + + if (vcpu->arch.pmu.access =3D=3D VCPU_PMU_ACCESS_FREE) + return false; + + pmu =3D vcpu->kvm->arch.arm_pmu; + mask =3D kvm_pmu_guest_counter_mask(pmu); + pmovs =3D __vcpu_sys_reg(vcpu, PMOVSSET_EL0); + pmint =3D read_pmintenset(); + pmcr =3D read_pmcr(); + overflow =3D (pmcr & ARMV8_PMU_PMCR_E) && (mask & pmovs & pmint); + + return overflow; +} diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index a40db0d5120ff..c5438de3e5a74 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -268,7 +268,7 @@ void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vc= pu, u64 val) * counter where the values of the global enable control, PMOVSSET_EL0[n],= and * PMINTENSET_EL1[n] are all 1. */ -bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu) +bool kvm_pmu_emul_overflow_status(struct kvm_vcpu *vcpu) { u64 reg =3D __vcpu_sys_reg(vcpu, PMOVSSET_EL0); =20 @@ -405,7 +405,7 @@ static void kvm_pmu_perf_overflow(struct perf_event *pe= rf_event, kvm_pmu_counter_increment(vcpu, BIT(idx + 1), ARMV8_PMUV3_PERFCTR_CHAIN); =20 - if (kvm_pmu_overflow_status(vcpu)) { + if (kvm_pmu_emul_overflow_status(vcpu)) { kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); =20 if (!in_nmi()) diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index 8c10ad05661bc..f1c66ce678840 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -408,7 +408,11 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) struct kvm_pmu *pmu =3D &vcpu->arch.pmu; bool overflow; =20 - overflow =3D kvm_pmu_overflow_status(vcpu); + if (kvm_vcpu_pmu_is_partitioned(vcpu)) + overflow =3D kvm_pmu_part_overflow_status(vcpu); + else + overflow =3D kvm_pmu_emul_overflow_status(vcpu); + if (pmu->irq_level =3D=3D overflow) return; =20 diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index de058a5347d18..4af8abf2dde0f 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -90,6 +90,8 @@ bool kvm_set_pmuserenr(u64 val); void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu); void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu); void kvm_vcpu_pmu_resync_el0(void); +bool kvm_pmu_emul_overflow_status(struct kvm_vcpu *vcpu); +bool kvm_pmu_part_overflow_status(struct kvm_vcpu *vcpu); =20 #define kvm_vcpu_has_pmu(vcpu) \ (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3)) --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oa1-f74.google.com (mail-oa1-f74.google.com [209.85.160.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 57ABE40F8CE for ; Mon, 4 May 2026 21:18:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929539; cv=none; b=BiVP+4Hwet8tgT/bT3nrbv1G0pR8ZFITtfmKZu4K+wMRHOLfHvGkLcCPakgD4FtTnyxmAXGr55/n5C+PSoOY11XHiokxoF4t1e8ahq13bvGaKEXusMX3YXJu4TNqsV7SFHgeu7KwQ1FRul4Xj4WM9dcM3q/yR7HIIvN9RXZ6cOw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929539; c=relaxed/simple; bh=84vobWOH05RGjv7LtGVjVHJaaRYadM6oijMz8EoqR4o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=oHgr7ZXog/O3z5JrF8wVpPHL1gFBYfnoGMPU5Spwp2yyQuG71ZOHtmFFzogCX+1Df4Tme1+01dYGBSkyFty5xtpKL4dXXUXVcGfvKzufkf5rPketczLIhsBossvYtpzRaOhmTvFu5NyiobJ0YWod6njoQf4YPVtbdVbYT8q4sDg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=LkIRgyhb; arc=none smtp.client-ip=209.85.160.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="LkIRgyhb" Received: by mail-oa1-f74.google.com with SMTP id 586e51a60fabf-43031704442so8265954fac.1 for ; Mon, 04 May 2026 14:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929531; x=1778534331; darn=vger.kernel.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=5s0tIXpB8hlNO6Tpc8lm3vOaXeUhQ8iprPjSFyACC84=; b=LkIRgyhbhlAPj1xtbL1HS9SUOUdCKzIm0UCRgyHP7lg5BZqXK6ir5pG4DbPN+RQZt2 mFwFCKmXxHFb1/+Gz2kZtpbJI9Icul1N5oLHHuJRJewuCQfVH8XvQtGE10St1FG3YHCd 35EGJRJvRN03G+JWGybocGUj2PcZyx5r/LZ8sfNq3QRZuC4i/X1Qb34jvtqO0/YtBpZR /ueIzEKMTR1SAR3siJVjuz8ZJbKw3VW9i24rAn8ZN/svxKycMhMSkhQ22nw4O11UMl96 Ddib9zQ/3iU/9kDI+We91SRh29c1hGeJ/+FeBjGBvPGi2T/OsU5ykituivEFgQyzKeYf IIYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929531; x=1778534331; 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=5s0tIXpB8hlNO6Tpc8lm3vOaXeUhQ8iprPjSFyACC84=; b=j3XJKPAf+pweTcgjgCZaqo31Htw9YAgnD+9BPv6mm6y655Dhy7lGp7EpUf3fpt9nYE e2QjVtzZ94syak9iy9f+sNlF81lBHiaKZHHkfnWJmiom7NTS72l0BbNag8xEYERHwh8i 5/UTA0lN/XFKT0IqiCTHSyfixYlHtFEuV52kXNk2Gn0DtOem/r62Sy0QVei8AET4cKVh j3afMDUUb8pdlfBgnWlpAcbE9An4atjb5aN5tShEA45NNqJd9ePXNQuoepuSEItN+Z5T xf/8PRdOEO99ZuNuyq/FssNcAAneq5Y0AAFOXI8q0gYCfymjp+4Axcs134j/s8DlhBlm byKg== X-Forwarded-Encrypted: i=1; AFNElJ+JHAQe32D/Tb7gABpCiW41OIhp5batQ04o77f8BEI48AtlKwhGQm6n9btVnYTxKsm0KOEmgyH7IcrI1Pk=@vger.kernel.org X-Gm-Message-State: AOJu0YwYyW1x+CnXEqBZ6T82eGBfn1HkeanJ6klZC5VMuOBpV9Jzk8Fb 8YgOume1DgrVujqJTO3nR8PYcFaYM6WV9E7fZyYRZYrq3TRHcY3I5AaJyzCupTKCENBSPq4fi2h zdfcniruRwecajq8pVQLEyVmKGg== X-Received: from ilbeh6-n2.prod.google.com ([2002:a05:6e02:4c06:20b0:4fb:4dad:95a4]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:211:b0:696:6585:a4b with SMTP id 006d021491bc7-696979cbddfmr5538431eaf.5.1777929530625; Mon, 04 May 2026 14:18:50 -0700 (PDT) Date: Mon, 4 May 2026 21:18:10 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-18-coltonlewis@google.com> Subject: [PATCH v7 17/20] KVM: arm64: Add vCPU device attr to partition the PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new PMU device attr to enable the partitioned PMU for a given VM. This capability can be set when the PMU is initially configured before the vCPU starts running and is allowed where PMUv3 and VHE are supported and the host driver was configured with arm_pmuv3.reserved_host_counters. The enabled capability is tracked by the new flag KVM_ARCH_FLAG_PARTITION_PMU_ENABLED. Signed-off-by: Colton Lewis --- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/include/uapi/asm/kvm.h | 2 ++ arch/arm64/kvm/pmu-direct.c | 35 ++++++++++++++++++++++++++++--- arch/arm64/kvm/pmu.c | 14 +++++++++++++ include/kvm/arm_pmu.h | 9 ++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 891433fe304ac..22b3985b978de 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -353,6 +353,8 @@ struct kvm_arch { #define KVM_ARCH_FLAG_WRITABLE_IMP_ID_REGS 10 /* Unhandled SEAs are taken to userspace */ #define KVM_ARCH_FLAG_EXIT_SEA 11 + /* Partitioned PMU Enabled */ +#define KVM_ARCH_FLAG_PARTITION_PMU_ENABLED 12 unsigned long flags; =20 /* VM-wide vCPU feature set */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/as= m/kvm.h index a792a599b9d68..3e0b7619f781d 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -436,6 +436,8 @@ enum { #define KVM_ARM_VCPU_PMU_V3_FILTER 2 #define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 +#define KVM_ARM_VCPU_PMU_V3_ENABLE_PARTITION 5 + #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 9693d9eb69daa..47fd143cf4ac7 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -44,8 +44,8 @@ bool kvm_pmu_is_partitioned(struct arm_pmu *pmu) } =20 /** - * kvm_vcpu_pmu_is_partitioned() - Determine if given VCPU has a partition= ed PMU - * @vcpu: Pointer to kvm_vcpu struct + * kvm_pmu_is_partitioned() - Determine if given VCPU has a partitioned PMU + * @kvm: Pointer to kvm_vcpu struct * * Determine if given VCPU has a partitioned PMU by extracting that * field and passing it to :c:func:`kvm_pmu_is_partitioned` @@ -55,7 +55,36 @@ bool kvm_pmu_is_partitioned(struct arm_pmu *pmu) bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu) { return kvm_pmu_is_partitioned(vcpu->kvm->arch.arm_pmu) && - false; + test_bit(KVM_ARCH_FLAG_PARTITION_PMU_ENABLED, &vcpu->kvm->arch.flags); +} + +/** + * has_kvm_pmu_partition_support() - If we can enable/disable partition + * + * Return: true if allowed, false otherwise. + */ +bool has_kvm_pmu_partition_support(void) +{ + return has_host_pmu_partition_support() && + kvm_supports_guest_pmuv3() && + armv8pmu_is_partitioned; +} + +/** + * kvm_pmu_partition_enable() - Enable/disable partition flag + * @kvm: Pointer to vcpu + * @enable: Whether to enable or disable + * + * If we want to enable the partition, the guest is free to grab + * hardware by accessing PMU registers. Otherwise, the host maintains + * control. + */ +void kvm_pmu_partition_enable(struct kvm *kvm, bool enable) +{ + if (enable) + set_bit(KVM_ARCH_FLAG_PARTITION_PMU_ENABLED, &kvm->arch.flags); + else + clear_bit(KVM_ARCH_FLAG_PARTITION_PMU_ENABLED, &kvm->arch.flags); } =20 /** diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index f1c66ce678840..add5e7da830b2 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -759,6 +759,19 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, str= uct kvm_device_attr *attr) =20 return kvm_arm_pmu_v3_set_nr_counters(vcpu, n); } + case KVM_ARM_VCPU_PMU_V3_ENABLE_PARTITION: { + unsigned int __user *uaddr =3D (unsigned int __user *)(long)attr->addr; + bool enable; + + if (get_user(enable, uaddr)) + return -EFAULT; + + if (!has_kvm_pmu_partition_support()) + return -EPERM; + + kvm_pmu_partition_enable(kvm, enable); + return 0; + } case KVM_ARM_VCPU_PMU_V3_INIT: return kvm_arm_pmu_v3_init(vcpu); } @@ -798,6 +811,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, stru= ct kvm_device_attr *attr) case KVM_ARM_VCPU_PMU_V3_FILTER: case KVM_ARM_VCPU_PMU_V3_SET_PMU: case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: + case KVM_ARM_VCPU_PMU_V3_ENABLE_PARTITION: if (kvm_vcpu_has_pmu(vcpu)) return 0; } diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 4af8abf2dde0f..131c4b8eec194 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -107,6 +107,8 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu); void kvm_pmu_put(struct kvm_vcpu *vcpu); =20 void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu); +bool has_kvm_pmu_partition_support(void); +void kvm_pmu_partition_enable(struct kvm *kvm, bool enable); =20 #if !defined(__KVM_NVHE_HYPERVISOR__) bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu); @@ -295,6 +297,13 @@ static inline u64 kvm_pmu_guest_counter_mask(void *kvm) =20 static inline void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovs= r) {} =20 +static inline bool has_kvm_pmu_partition_support(void) +{ + return false; +} + +static inline void kvm_pmu_partition_enable(struct kvm *kvm, bool enable) = {} + #endif =20 #endif --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0EA6426697 for ; Mon, 4 May 2026 21:18:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929541; cv=none; b=eWvaAZAj5mpN5epfGPsziJyAwJ2iwfltoELDY774qkHR4waJGX+FYOGhrvSB5m8GHU3+xaa9RcAoj+TlorW1iVRO2ItdKNVaapXZKRyPpInNF1pcQIP1mrQ3CYQAADkz4+ETX7HM6XnqOttPO44J/5dFURbVovbzhmqDxUAYaP0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929541; c=relaxed/simple; bh=/Xt6rnYnGFBq52V4bBzNz6QqRctwemMB97jkZH74ots=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OjHEGlgTgAuMj6YmvQiAIyZUhBlW6Nq1iLhDdUG9ur3tsxo1AD0oi8dgtbkCuh4wlEMwM3Zqru1V1RVWVBBK8eWlmRlaIcGRlGYZc6ZJj4XV8d/7aG9u5/Bg4DijHIHHK0WZbgLoMluKO8TI5OmHHx+XYocsZqLycsxtk63mNMs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=GAA6nA7x; arc=none smtp.client-ip=209.85.161.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="GAA6nA7x" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-696133ef271so8845042eaf.2 for ; Mon, 04 May 2026 14:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929532; x=1778534332; darn=vger.kernel.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=59TeqgAKhZvxIms330HaGI06DkqMrz/YA7lL8opNhPQ=; b=GAA6nA7xd+hXrpdjcybtVknsF0NI8V4ehjMD8DFQ6IOV0BprIcDwQ7/Pve2krzXgDJ k8W7ng9N72p6QUJysxBEiK6mBgZEZC3S4SWueXIKTT/j2UOFG3zbdrG3Tli8RSJpDkZu 27Z3+aUmi4Frzg09wlDzQDrWw7cLDH7LIgi+s+L+UHSYJAO8gtafX7ENT83uaz5+uAKp Um8w+5H07mziO1vDOlU2WlsYGdE3sCSGbhh4/sqfkFG3pZ7B/HZ36Oy/XwNoIk3mCiuz 67fpBa2FWsRM7eUuZQtennVqoZQ1bKOZsTN8s1/m5GyKOMc2uPoMzkqn+cfRgAzuJNWg aQHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929532; x=1778534332; 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=59TeqgAKhZvxIms330HaGI06DkqMrz/YA7lL8opNhPQ=; b=AqmuzPUoX53GD1L+brGwiP1Ovz4HwpeYBcJkSPNlndYiI5ENoj+lRhQrDXX7Mv4eyQ zm9kRb1HmX00QRrm5FByT4EfQXtDA/mOsvZLD1CsITmAPqhQpEBv0utKppdE7tTatmiQ plOtfcZJFbaWDZ81sfqXR+hQBMeUboxTZ6GiTGYk+NOO6TgMglTtOA49AIDrSnfFK0Z2 0G+763VPhXc9lYk4lTm5LTty9m/uwWcyGCPjnqF0H5vmZCjWI5GHGWVe4w7vtZ3Yf1L/ 66f7XkQH9c5DTcetBq2Kil5UhgAc9XATXEos1o9hU89Xu7t5SXM7U4AshM14v1/tYbKM Mq4w== X-Forwarded-Encrypted: i=1; AFNElJ+RANe10a9Zy8pD6smOD6ZmReUg70fZPNJHvW+3iEYMY33HG5oWpkZeBvXUokei8EMSNIdYkdhHTr99zto=@vger.kernel.org X-Gm-Message-State: AOJu0Yz6FbPWh3+Lo7EkvUlncjNb2sARJnT0zxeMLrOspwArinQ89OR8 RET/UF0/lJOlBKFHO8zAW7yQgezWrfy0p08psdp8WggaWL2bKPqvCjOC9RWeVMjwRxEEmBykLdY DrKdVAsNONE0iFX7hLiJr7m+RVQ== X-Received: from ildm3.prod.google.com ([2002:a92:8703:0:b0:4fa:5514:a367]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:629:b0:696:757d:1942 with SMTP id 006d021491bc7-69697c60c82mr5023525eaf.32.1777929531976; Mon, 04 May 2026 14:18:51 -0700 (PDT) Date: Mon, 4 May 2026 21:18:11 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-19-coltonlewis@google.com> Subject: [PATCH v7 18/20] KVM: selftests: Add find_bit to KVM library From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some selftests have a dependency on find_bit and weren't compiling separately without it, so I've added it to the KVM library here using the same method as files like rbtree.c. Signed-off-by: Colton Lewis --- tools/testing/selftests/kvm/Makefile.kvm | 1 + tools/testing/selftests/kvm/lib/find_bit.c | 1 + 2 files changed, 2 insertions(+) create mode 100644 tools/testing/selftests/kvm/lib/find_bit.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selft= ests/kvm/Makefile.kvm index 6471fa214a9f9..79a45807865b5 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -5,6 +5,7 @@ all: =20 LIBKVM +=3D lib/assert.c LIBKVM +=3D lib/elf.c +LIBKVM +=3D lib/find_bit.c LIBKVM +=3D lib/guest_modes.c LIBKVM +=3D lib/io.c LIBKVM +=3D lib/kvm_util.c diff --git a/tools/testing/selftests/kvm/lib/find_bit.c b/tools/testing/sel= ftests/kvm/lib/find_bit.c new file mode 100644 index 0000000000000..67d9d9cbca85c --- /dev/null +++ b/tools/testing/selftests/kvm/lib/find_bit.c @@ -0,0 +1 @@ +#include "../../../../lib/find_bit.c" --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE65A33F59C for ; Mon, 4 May 2026 21:18:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929541; cv=none; b=pXgvMsR3t8oQcw54ATCji2FJ8kMJqL81BoloIYG6BzjBISQQu9rE928rirXiS2W9EUL1ixRMFn7HEsriwNghN/M6S70T7aT/6nWCWdO1WIIyZ3eDaWTFEQIOPc8hbAw0ORMVZ1TMOf938DrYtZCf0ib9bv87XgOH7gPgDo7NNis= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929541; c=relaxed/simple; bh=5F70qgzxp+5sKP5uTsCVeijSZjWYYQXTinyxMntx/dU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Kb3YAs8oOxEGKeANb8mvmVUk0JOqcL4oTUiKKTekIHUUpUZ1xPzaiPqM1mKpGVwFZWqfvSs7Y53z//nbKnHcQFvAaDuIEyLOr4iAtO+A0eUA4ADR9JfPSeG22qmBQ6WI8q1KXsVMmh6UrD/qQALit83n/rAOHrpHvoIwh4n84Dg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SSzq4xVC; arc=none smtp.client-ip=209.85.161.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SSzq4xVC" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-696906ddd6eso3158294eaf.2 for ; Mon, 04 May 2026 14:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929533; x=1778534333; darn=vger.kernel.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=A1LC20y4taYV29x8hD4zY85wXN5CKP6Npg+wcjsGL1w=; b=SSzq4xVCRP34Bj4s/hf1NucLHEybXd33Yly0a+wUTUSKSrph4Zp8Am6l29CtFljrqY Dd/MemVw05pHerh5zRIgBUyHEvOCq40AyaVA0gWNsEQW2FCt9+ty7musDk9Xuf82WQG4 2S90gddFKMe+hPVIw8OdFG68vGt9RD3PqaDnbIFev5qf4iipKfKWhtvYGrk1anEn+J7G nXCTMDPSEIpx5RU2DsSbo+Wg/nCHo8Mu8WnefPtNmj6FayY7nk8+USaUZH+xFnrhpm3W u09Y9eZFmhWxXf4cg8oKs4leUQN4KWhMqlu0rIPdRWGYC37XcApNBr1/3YjP+ZbFJrm7 AuQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929533; x=1778534333; 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=A1LC20y4taYV29x8hD4zY85wXN5CKP6Npg+wcjsGL1w=; b=bK+qRn/eBf7QGUA+uJ2xuUiYkAaUBf0jOX5uUXhBPJdLhEZUgaz6ej1/5Z5A6/pM/0 QHwqgzwJtRjREhcrHXSatoyUtXqXkvHjIjGnf99u1IqYyZl6Q4r3FPPjzIQn4JGct6q1 r9jk+IJpduy58ISErKh7rh8qKHjeD2Ngy4fe+oCDlo2aIH7PPPmnctomjHs7yy9+a4A7 hX2yXNTcnwhbz2iQVkhDF972VQmrnW51Uczf7wf54LfUo7JwLSLV0iHAnk6t5v97IqWb UxT7tPgwQmKqoLKb1B+0q2flDb38hj0wRJIUk4WIIvlTuRsZCm0qmdQPuqLLixsyAEE+ /TiA== X-Forwarded-Encrypted: i=1; AFNElJ/FCNDcwWdBUMARRct+Pejfd/Mq+6CGF/RO9ugGSG37sIVvJvuAoaWHz1b4IkqrZjPdvx/9CZLPVugSQUM=@vger.kernel.org X-Gm-Message-State: AOJu0YyMKVegJoFaCKiHAlE4QpfjrcecSUvwEtxis+E4+1oY8mYzjJEa Uw59xP5zQjeF+uKetyvuWnH4QWx0uBIgf3pI+w3LULYQx/6w2+MbPLdoe1nHnRTHfyrkZqRT8jg rrYbpOcFQ/534YCMldeWioxkvJw== X-Received: from ilql7.prod.google.com ([2002:a92:d947:0:b0:4fe:69b2:404b]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2014:b0:694:6e61:8de0 with SMTP id 006d021491bc7-6998d24837amr140937eaf.30.1777929533288; Mon, 04 May 2026 14:18:53 -0700 (PDT) Date: Mon, 4 May 2026 21:18:12 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-20-coltonlewis@google.com> Subject: [PATCH v7 19/20] KVM: arm64: selftests: Add test case for Partitioned PMU From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Rerun all tests for a Partitioned PMU in vpmu_counter_access. Create an enum specifying whether we are testing the emulated or Partitioned PMU and all the test functions are modified to take the implementation as an argument and make the difference in setup appropriately. Signed-off-by: Colton Lewis --- .../selftests/kvm/arm64/vpmu_counter_access.c | 94 ++++++++++++++----- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tool= s/testing/selftests/kvm/arm64/vpmu_counter_access.c index ae36325c022fb..9702f1d43b832 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -25,9 +25,20 @@ /* The cycle counter bit position that's common among the PMU registers */ #define ARMV8_PMU_CYCLE_IDX 31 =20 +enum pmu_impl { + EMULATED, + PARTITIONED +}; + +const char *pmu_impl_str[] =3D { + "Emulated", + "Partitioned" +}; + struct vpmu_vm { struct kvm_vm *vm; struct kvm_vcpu *vcpu; + bool pmu_partitioned; }; =20 static struct vpmu_vm vpmu_vm; @@ -399,7 +410,7 @@ static void guest_code(uint64_t expected_pmcr_n) } =20 /* Create a VM that has one vCPU with PMUv3 configured. */ -static void create_vpmu_vm(void *guest_code) +static void create_vpmu_vm(void *guest_code, enum pmu_impl impl) { struct kvm_vcpu_init init; uint8_t pmuver, ec; @@ -409,6 +420,13 @@ static void create_vpmu_vm(void *guest_code) .attr =3D KVM_ARM_VCPU_PMU_V3_IRQ, .addr =3D (uint64_t)&irq, }; + bool partition =3D (impl =3D=3D PARTITIONED); + struct kvm_device_attr part_attr =3D { + .group =3D KVM_ARM_VCPU_PMU_V3_CTRL, + .attr =3D KVM_ARM_VCPU_PMU_V3_ENABLE_PARTITION, + .addr =3D (uint64_t)&partition + }; + int ret; =20 /* The test creates the vpmu_vm multiple times. Ensure a clean state */ memset(&vpmu_vm, 0, sizeof(vpmu_vm)); @@ -436,6 +454,15 @@ static void create_vpmu_vm(void *guest_code) "Unexpected PMUVER (0x%x) on the vCPU with PMUv3", pmuver); =20 vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &irq_attr); + + ret =3D __vcpu_has_device_attr( + vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_ENABLE_PARTI= TION); + if (!ret) { + vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &part_attr); + vpmu_vm.pmu_partitioned =3D partition; + pr_debug("Set PMU partitioning: %d\n", partition); + } + } =20 static void destroy_vpmu_vm(void) @@ -461,13 +488,14 @@ static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t = pmcr_n) } } =20 -static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters,= bool expect_fail) +static void test_create_vpmu_vm_with_nr_counters( + unsigned int nr_counters, enum pmu_impl impl, bool expect_fail) { struct kvm_vcpu *vcpu; unsigned int prev; int ret; =20 - create_vpmu_vm(guest_code); + create_vpmu_vm(guest_code, impl); vcpu =3D vpmu_vm.vcpu; =20 prev =3D get_pmcr_n(vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0))); @@ -489,7 +517,7 @@ static void test_create_vpmu_vm_with_nr_counters(unsign= ed int nr_counters, bool * Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_= n, * and run the test. */ -static void run_access_test(uint64_t pmcr_n) +static void run_access_test(uint64_t pmcr_n, enum pmu_impl impl) { uint64_t sp; struct kvm_vcpu *vcpu; @@ -497,7 +525,7 @@ static void run_access_test(uint64_t pmcr_n) =20 pr_debug("Test with pmcr_n %lu\n", pmcr_n); =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, impl, false); vcpu =3D vpmu_vm.vcpu; =20 /* Save the initial sp to restore them later to run the guest again */ @@ -531,14 +559,14 @@ static struct pmreg_sets validity_check_reg_sets[] = =3D { * Create a VM, and check if KVM handles the userspace accesses of * the PMU register sets in @validity_check_reg_sets[] correctly. */ -static void run_pmregs_validity_test(uint64_t pmcr_n) +static void run_pmregs_validity_test(uint64_t pmcr_n, enum pmu_impl impl) { int i; struct kvm_vcpu *vcpu; uint64_t set_reg_id, clr_reg_id, reg_val; uint64_t valid_counters_mask, max_counters_mask; =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, false); + test_create_vpmu_vm_with_nr_counters(pmcr_n, impl, false); vcpu =3D vpmu_vm.vcpu; =20 valid_counters_mask =3D get_counters_mask(pmcr_n); @@ -588,11 +616,11 @@ static void run_pmregs_validity_test(uint64_t pmcr_n) * the vCPU to @pmcr_n, which is larger than the host value. * The attempt should fail as @pmcr_n is too big to set for the vCPU. */ -static void run_error_test(uint64_t pmcr_n) +static void run_error_test(uint64_t pmcr_n, enum pmu_impl impl) { - pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); + pr_debug("Error test with pmcr_n %lu (larger than the host allows)\n", pm= cr_n); =20 - test_create_vpmu_vm_with_nr_counters(pmcr_n, true); + test_create_vpmu_vm_with_nr_counters(pmcr_n, impl, true); destroy_vpmu_vm(); } =20 @@ -600,11 +628,11 @@ static void run_error_test(uint64_t pmcr_n) * Return the default number of implemented PMU event counters excluding * the cycle counter (i.e. PMCR_EL0.N value) for the guest. */ -static uint64_t get_pmcr_n_limit(void) +static uint64_t get_pmcr_n_limit(enum pmu_impl impl) { uint64_t pmcr; =20 - create_vpmu_vm(guest_code); + create_vpmu_vm(guest_code, impl); pmcr =3D vcpu_get_reg(vpmu_vm.vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)); destroy_vpmu_vm(); return get_pmcr_n(pmcr); @@ -614,7 +642,7 @@ static bool kvm_supports_nr_counters_attr(void) { bool supported; =20 - create_vpmu_vm(NULL); + create_vpmu_vm(NULL, EMULATED); supported =3D !__vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_C= TRL, KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS); destroy_vpmu_vm(); @@ -622,22 +650,46 @@ static bool kvm_supports_nr_counters_attr(void) return supported; } =20 -int main(void) +static bool kvm_supports_partition_attr(void) +{ + bool supported; + + create_vpmu_vm(NULL, EMULATED); + supported =3D !__vcpu_has_device_attr(vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_C= TRL, + KVM_ARM_VCPU_PMU_V3_ENABLE_PARTITION); + destroy_vpmu_vm(); + + return supported; +} + +void test_pmu(enum pmu_impl impl) { uint64_t i, pmcr_n; =20 - TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); - TEST_REQUIRE(kvm_supports_vgic_v3()); - TEST_REQUIRE(kvm_supports_nr_counters_attr()); + pr_info("Testing PMU: Implementation =3D %s\n", pmu_impl_str[impl]); + + pmcr_n =3D get_pmcr_n_limit(impl); + pr_debug("PMCR_EL0.N: Limit =3D %lu\n", pmcr_n); =20 - pmcr_n =3D get_pmcr_n_limit(); for (i =3D 0; i <=3D pmcr_n; i++) { - run_access_test(i); - run_pmregs_validity_test(i); + run_access_test(i, impl); + run_pmregs_validity_test(i, impl); } =20 for (i =3D pmcr_n + 1; i < ARMV8_PMU_MAX_COUNTERS; i++) - run_error_test(i); + run_error_test(i, impl); +} + +int main(void) +{ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); + TEST_REQUIRE(kvm_supports_vgic_v3()); + TEST_REQUIRE(kvm_supports_nr_counters_attr()); + + test_pmu(EMULATED); + + if (kvm_supports_partition_attr()) + test_pmu(PARTITIONED); =20 return 0; } --=20 2.54.0.545.g6539524ca2-goog From nobody Fri Jun 12 15:58:04 2026 Received: from mail-ot1-f73.google.com (mail-ot1-f73.google.com [209.85.210.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 058DD40FD93 for ; Mon, 4 May 2026 21:18:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929543; cv=none; b=V65haRvA8EnGrsWkyH9AkPHCtelJ5CrulU0cINKta1z4MtDB1WYW7hUJWA3damF08sqc986kLOhPT+gywaR5yfmPbsh6uB8lwA5oRhPwl5B8KvMpHUiywhj8eniz64vMfYUoTFKVmEocmDFUkSC1+Pz+VouE80UiIYa9N5v5bak= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777929543; c=relaxed/simple; bh=z22FJ/RoHsM+jeuWzpMU9byGN80HsM7C3iFbEeU0P2Q=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=iKZ2K9SvKDaw5ArI1i57durWW2Blp2f1vjfgPK/ZFnHKH+oaxL0VoNPMPziIRfTHrwxRgTn9Juo77GnH5qnQeBaSD5+aXDIuF5gatsiSxN6copOhfqWaj82ww9Tui4kYC1MTuNSRr1LGjLIPwid6xOlz47GD8oMU/KcqN8KM7GM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=cLtfez+A; arc=none smtp.client-ip=209.85.210.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--coltonlewis.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="cLtfez+A" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7dcd9f8dbe2so7980984a34.1 for ; Mon, 04 May 2026 14:18:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777929535; x=1778534335; darn=vger.kernel.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=BjlPgXNwXeFHjDTHfAecQAjmNwOoUuZdzNZWTJemW0E=; b=cLtfez+AcMC+GjbVP/sLegG4Ev2Kqp0rj95GdVnSM5UIqNdYQvFaVWnpV1f4Mhou0u mMc6kkuCa+4rH95H0nkPzlN4l48xKX93DikxuGC7iGOb3SozrInCm7ZkhzGVi0IxJaac 8vx1n3G7n5CuTEgzf9mk3f//CLemcxPL3xrr5tvRZAbWlKABI90N6jGyUZ9MxogyS0dy sJ72O2Bqomq8dvG89WMxlwRPToslhwLOEM7iCUzTnduOAdy834MG1XsLqoBWzxA+tC0U 46AmUcRv43DKxqnqykvU9+XcVAVDu4vD04754WPXRphv9PC2Nnz2dj6z/tr5J1E3qQWY 3obg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777929535; x=1778534335; 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=BjlPgXNwXeFHjDTHfAecQAjmNwOoUuZdzNZWTJemW0E=; b=SDzZpXKVxLHu6tqqH4C/B2psYsap40qgMMuuc77+7goR4irxidtwx7BubXe3PB6Rvs L1CrrKi/QCyWRfzVx+33ZkO7kU4JFc1gzy2e/2AI4kGdVENR2+izN+7a/kmGawRWs7Gy ZlbfzFg1baMNvu1kj+vVs0r8wGJUlGpk5m8I3wpStKCNgt8RW4LI9nfw0EzkwzR1wXK+ g4VfnQ0Ca94nBviId6IjcTOyRhn5iYKTHiWX4h0yJMIMiG226z0MRDeji50+am3CYLKQ 4qBUrFNKcFT0l2zlIngelDuGaGSiIvbCfurKivDSzb5+ONj9UaQmUv87l7i95iX1WMOO p+uw== X-Forwarded-Encrypted: i=1; AFNElJ+UKU4sU57UUvHPmo7ksPbDQwWh8Xr1VcX8RmqlaYbt34YtlPBQtnH7vTxeWiQmUpwlyU1qSgAaQNMe0JA=@vger.kernel.org X-Gm-Message-State: AOJu0YyoQgWDKQOwns4JmF1ZiAa6YqeIwa/g9WqTZVjH2+BSDSGGjFng Xfx7+vQUQXzZlHNUWsPUXl0IHykV1iVUUMqjz5jqGh3Rf0FVEoIZisIa3tkyXj9PnJ71jBatvMQ MSMlscoJ4C7kgQecf/t1umyxFpw== X-Received: from ilqt1.prod.google.com ([2002:a92:cc41:0:b0:4fd:69a2:6072]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:81c6:b0:694:8fb2:2c35 with SMTP id 006d021491bc7-6998a59a043mr161048eaf.2.1777929534601; Mon, 04 May 2026 14:18:54 -0700 (PDT) Date: Mon, 4 May 2026 21:18:13 +0000 In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504211813.1804997-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260504211813.1804997-21-coltonlewis@google.com> Subject: [PATCH v7 20/20] KVM: arm64: selftests: Relax testing for exceptions when partitioned From: Colton Lewis To: kvm@vger.kernel.org Cc: Alexandru Elisei , 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 , Ganapatrao Kulkarni , James Clark , 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-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Because the Partitioned PMU must lean heavily on underlying hardware support, it can't guarantee an exception occurs when accessing an invalid pmc index. The ARM manual specifies that accessing PMEVCNTR_EL0 where n is greater than the number of counters on the system is constrained unpredictable when FEAT_FGT is not implemented, and it is desired the Partitioned PMU still work without FEAT_FGT. Though KVM could enforce exceptions here since all PMU accesses without FEAT_FGT are trapped, that creates further difficulties. For one example, the manual also says that after writing a value to PMSELR_EL0 greater than the number of counters on a system, direct reads will return an unknown value, meaning KVM could not rely on the hardware register to hold the correct value. Signed-off-by: Colton Lewis --- .../selftests/kvm/arm64/vpmu_counter_access.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tool= s/testing/selftests/kvm/arm64/vpmu_counter_access.c index 9702f1d43b832..27b7d7b2a059a 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -38,10 +38,14 @@ const char *pmu_impl_str[] =3D { struct vpmu_vm { struct kvm_vm *vm; struct kvm_vcpu *vcpu; +}; + +struct guest_context { bool pmu_partitioned; }; =20 static struct vpmu_vm vpmu_vm; +static struct guest_context guest_context; =20 struct pmreg_sets { uint64_t set_reg_id; @@ -342,11 +346,16 @@ static void test_access_invalid_pmc_regs(struct pmc_a= ccessor *acc, int pmc_idx) /* * Reading/writing the event count/type registers should cause * an UNDEFINED exception. + * + * If the pmu is partitioned, we can't guarantee it because + * hardware doesn't. */ - TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->read_cntr(pmc_idx)); - TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->write_cntr(pmc_idx, 0)); - TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->read_typer(pmc_idx)); - TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->write_typer(pmc_idx, 0)); + if (!guest_context.pmu_partitioned) { + TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->read_cntr(pmc_idx)); + TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->write_cntr(pmc_idx, 0)); + TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->read_typer(pmc_idx)); + TEST_EXCEPTION(ESR_ELx_EC_UNKNOWN, acc->write_typer(pmc_idx, 0)); + } /* * The bit corresponding to the (unimplemented) counter in * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers should be RAZ. @@ -459,7 +468,7 @@ static void create_vpmu_vm(void *guest_code, enum pmu_i= mpl impl) vpmu_vm.vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_ENABLE_PARTI= TION); if (!ret) { vcpu_ioctl(vpmu_vm.vcpu, KVM_SET_DEVICE_ATTR, &part_attr); - vpmu_vm.pmu_partitioned =3D partition; + guest_context.pmu_partitioned =3D partition; pr_debug("Set PMU partitioning: %d\n", partition); } =20 @@ -511,6 +520,7 @@ static void test_create_vpmu_vm_with_nr_counters( TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret)); =20 vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_= INIT, NULL); + sync_global_to_guest(vpmu_vm.vm, guest_context); } =20 /* --=20 2.54.0.545.g6539524ca2-goog