From nobody Sun Jun 14 01:35:13 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 95876377ED9 for ; Fri, 12 Jun 2026 19:29:12 +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=1781292554; cv=none; b=QGsosEC+RK7KtvK6J0AQK7975Lu+heqNe3YoEUdODmVuuDhTrsX8imDhlBx0CQetafGxD2FYehiwSSWyRcWPacg9TGPMw1MBhwcR9rf8FvnwEdaYhEH+CiNdwp57vrtoqY+hvBFTB8DwMxSpYXH9lantZKbz00E9t16WNNKo0V4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292554; c=relaxed/simple; bh=5kGKr+m+2SakCtheR8UcEY2IRqG2QSoWSyyeWDBZdSA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WTqd9MYA0JfJn9WfFHBqVP7U7PP34Sx0hJAMX2Rnz6pCD2ZfzcQJDqTWS03/crw7O8IdFMDR9A1RvP7F9KsIMGivLnfmx0BDl+RvdlWGeAwD/jf/VXFIvq4QLlWSW9js0N5EvoYKhC53gSl7fC+SSwAZT6EK2sYiKUyyv1YpDSI= 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=s8Zfw3qF; 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="s8Zfw3qF" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-48637dce961so1273884b6e.0 for ; Fri, 12 Jun 2026 12:29:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292552; x=1781897352; 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=HlOJEoz94FuxFi6Nw0eWYLZk82W0XKPPEF+zFyUMrwY=; b=s8Zfw3qF9fCxp9p7wpSOuKQkcv4zEmEt2xL78cUcoOCUv3UUoG6ZGmxN4s1xwHf5d9 px/Q+Jq516VmQSk/8eGvkZJyyDShxJrS+vWluXgkDa9pdSm410JYJtistYfHrlr00j7w lIuLZRbb1jxT6NQXwQPfYa3x5B/ms6F19YzmT7Ds+nAhEhRPwwOrdX7BwLalcL+AFHl3 Ur/+kKRGC63xJCrmadjRoRtM1CuBeImL9BYHCyt8jDSi4hYVfVUzJVhfWACfPJ58EECo bdG+H2dPGfhFzZ2pIrTIl4RXnr0IowvMjW0/3JbVQ+tw69f0WbNQAOKMGLoFB92f0WBM /u3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292552; x=1781897352; 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=HlOJEoz94FuxFi6Nw0eWYLZk82W0XKPPEF+zFyUMrwY=; b=VOc1b6pgP1aZWLX8RbYP/q9vsdxitgxmsyaFUw67o/VF4jEuOFV/X9CZfjEKbfq/ef bzPYEMqrJbKjlgezrfY4wsMRZUNxd9+CFb+T+BSPRFVDOK5ACsIHHj+HqLnEQJhSWNZf qoDOnKMkUX5nieWuJWoZYOSOTwwIxhkxdtDI3iqNJECEPCGRN26xWsr8MSIOd8MOyz9s 0ZDgIRYyH50DRcp74moGXgd6kLEdSkGsIO6DzPhTx+9uw5fIjChwa19A45KgqAdGTPl0 LaalChWmkLg3yzdRv01AM6oJFhiORhEwNdR1oNg1KwbWV00hC8GfIWCqBGNDaGL539nj zxig== X-Forwarded-Encrypted: i=1; AFNElJ8b95FjBZfHBj0la7l1c08jaRprb5X6mIyl1hP4v911S0of5eK40ffge1csXSv/icDnNh9zIVEPNM1Iw64=@vger.kernel.org X-Gm-Message-State: AOJu0YxhhctP/MCo1+TH6L3GddeC0cZuvyvNEGtPmWMFfuwxBrx/YiAa 8oCqpbwa3h8J1/zFCuKnef7FKv31LZYBtadq4M02sFZxobQd6ylbeCIfAwdnM2fj004jG4BS/j0 TUrNlysP3AvicVtdbyFnuZSWtwA== X-Received: from ilb5-n2.prod.google.com ([2002:a05:6e02:5305:20b0:500:2417:f074]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1913:b0:486:45c7:34c5 with SMTP id 5614622812f47-487419e6d1emr682785b6e.17.1781292551319; Fri, 12 Jun 2026 12:29:11 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:49 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-2-coltonlewis@google.com> Subject: [PATCH 01/21] 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 | 10 +++++++++- arch/arm64/kvm/sys_regs.c | 3 ++- arch/arm64/tools/cpucaps | 1 + arch/arm64/tools/sysreg | 6 +++--- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 6d53bb15cf7bb..096545a6e4043 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -77,7 +77,7 @@ #include #include =20 -#include +#include #include #include #include @@ -560,6 +560,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), @@ -2965,6 +2966,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 fa5c93c7a1352..c52873a6f91ed 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -3326,7 +3326,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 811c2479e82d6..f8fb4a6395428 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 6c3ff14e561e6..2d5cbc8ced114 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1679,9 +1679,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 8910B3AA1BD for ; Fri, 12 Jun 2026 19:29:13 +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=1781292555; cv=none; b=hgq9un2YMqeO2g+cEgiwvwYtIDjPKc7h/5DS1aXghvi3u2KIEyPjA3U9sJFI0gX3ZhBSDp29Mh48RQTANBn3Ajd5neawDlWd+/GfcQFfSWvnyz75GTIItIsP6ofoEbK56B/CenYOsxyCxVIMpGh9aYbBHf6bKqVuzlgmW1VEyss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292555; c=relaxed/simple; bh=owKPQr8eBHyCh9h5WkLUimZ+AjR4LpalKvHxfPcTXvQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sPxot1A+4YvCRc+FRcUSdln0J+sCFoTK1TUcsFz9VvVAmzfT3jX2ByNm1W1Qt9Z7QXzIvGYt50gi2e/Mq17pgHGjCMlknKI4Pj/Uox99aRk/TmHvcjCb3fZtFxV+I4HOwf5EBYnRyz2AOHb+Nhi/evXp5EoDzNeriiwPwA9GP9Q= 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=AHm3IQY9; 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="AHm3IQY9" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7e7271fad26so3576952a34.0 for ; Fri, 12 Jun 2026 12:29:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292552; x=1781897352; 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=JOP4oEaU0YzeukjVbsQ2eL5dXi7XZrwslM0o9h4B8J4=; b=AHm3IQY9Rc4tXjzScLg2ucWyNFFihSdel4Lb84CI+4hVmp5+2HXYPaeWS5i7tXXmwg VjpG3PS5oEAvwytfQ/ICe5sa/Te5RJBPqp06oFChtNJoxU0g5+G/hWY1WpVvp0vZpLKs 95FSjL081IWcfOnAYH2nL32PyNWDNoKFkpU7shLs+eaizGF6nohrldkCTKZiAxNWgb1G bUYaxgkfVZWO+qjWDqpPtmcqSfZ+pTxc+LwROmYAcNXvZN22jYN6OhB6HLhmKjUuOprR OpBx1C30vY/g9NV/DtekZJuvHvikHiyTRAQaaNTF1BJUOCJfUqET80F2+Jp1pA5t2gzv nQCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292552; x=1781897352; 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=JOP4oEaU0YzeukjVbsQ2eL5dXi7XZrwslM0o9h4B8J4=; b=Y1LPC6xSrVBA5kv6uwiqHgE/yEEEmV1pIdtmr7OncQPveQH1IthZW8kcCBSTMfCmF6 6MGaSGerrhE/pp7vhTRc5qx99HY75Ze9VvNGDgvYrlofAZp69kQ7+oQTGJF30Mz5tXtm +o7tzWM+olY2eUQJBhqhTrndXcl9jz4Krg99piMmQS/DLF5cfjiw7niiTPQ46JU4cddY /UiKgx7nTcKZmuDDFXKssnWfFiO1+STpDvZq1xwEizmLrGruSk8wNcyWAl5UnD5aVfxs kn8rlqzPoEU+PGRHUla7ynQl8W1X4uMG8Y+R9qzePq1vMo93zsLyVkqNufy5Fcjax1p5 U+aQ== X-Forwarded-Encrypted: i=1; AFNElJ+8BwlYPbFiTdcyCBeb5n22oeaQV8oHAyk1yVUPEFW7uILQKQQuX/OwhwI5kUhInZWJTNNAz6D9xtFeq7I=@vger.kernel.org X-Gm-Message-State: AOJu0Yw12qrX1F04O8QSq9hzRYFdjMFNpR/QYvEoObzBXq0jsJiWWAiS yWdxkCs+RpekOSv+ZkcO29k8F6QE7ePzrduoDWMdRMHcOvANnSoXkP+qzwGA9yr967LV9q7Y5aQ kl2IK2V15xuA6G4Occ6W/iDYAqQ== X-Received: from ilkj13.prod.google.com ([2002:a05:6e02:eed:b0:500:7845:86d8]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:5146:b0:485:4f07:eefc with SMTP id 5614622812f47-4872de6bf08mr2751851b6e.15.1781292552277; Fri, 12 Jun 2026 12:29:12 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:50 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-3-coltonlewis@google.com> Subject: [PATCH 02/21] 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 a49042bfa801f..0d7a620c69ee2 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1480,25 +1480,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 0a36a3d5c8944..ec74a58905712 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -9,12 +9,22 @@ =20 #include #include +#include =20 #define KVM_ARMV8_PMU_MAX_COUNTERS 32 =20 /* PPI #23 - architecturally specified for GICv5 */ #define KVM_ARMV8_PMU_GICV5_IRQ 0x20000017 =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 */ @@ -69,6 +79,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); @@ -162,6 +175,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 91A743ABDA5 for ; Fri, 12 Jun 2026 19:29:14 +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=1781292560; cv=none; b=s58qNZeE1qxo0PV9KV1jgmUcdD4BVIp/qDCuSZrFVTrd21vyoUBAJK/twrEjElW+R/ahpqFCsY7naO8Fi9TBT2dA+KC7Upuafx61GwzLwTR2oMvUKTeDyKFwqEBDzyeBQVkaMwhd+KYoyIIoIvHcuAzxnD6dMwIlMQzu557K6ig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292560; c=relaxed/simple; bh=6ryzmsfHN7lXfihDbezHlHfc5uHQw/IQibsxQYgMjOU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mt5QZAem2XEpXQND+8BEbmhPTdLFeVa7dTWvP5oM80ZAR9aj+dywGDgxdn9VugpZtrYskV9gXnDw6oI/KFSgZck+JsSkrkPgWB5+9Wq+GxOCYI9SwFgjG6zU+UlQcQvDX6HALvD6zfw2vwACKAVgURnfERnXsFXGRR/wv7p82Ec= 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=cvtlWZtt; 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="cvtlWZtt" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-4863c8d459eso1285482b6e.3 for ; Fri, 12 Jun 2026 12:29:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292553; x=1781897353; 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=sFxgSJgDjgxmvJ6vBXlt0HNLmV/Dxxr2yDT0NLGK7kA=; b=cvtlWZttHAywLEkgMAv+dlYtIYBvaQwQHu0UugqeJ6+GFlPSJlp7fAuJccc2QnVUx+ ynnPjl3gfJ5+pu/bffP7cKky3woklQ2dgfJ98VYdremIKS3FoZHm20tp1VrlUH7b/itH 56BwfXK5fMX0zRWQwPI5gz4vCW0Kt4Y3ddaTtVJJH67XBWJ2vChJmxe8VWBA+n70Gz1M Yg8GQ92pn7N78O98PL/GsMSGzCQW11msGSuyLzn025GtOOl9pTRsRGonmqGq8HBOTKrZ BnrvXtTDIBcpnKE1VCEEU63ijMe8P8Amlufi5Ab87hVkxM2JxGWs+fcBjLjhnDaz3R1q G2vw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292553; x=1781897353; 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=sFxgSJgDjgxmvJ6vBXlt0HNLmV/Dxxr2yDT0NLGK7kA=; b=KBKWyYApMtUz3C7Ibz9IVB5VIuGOcd+IBKZAZLZPm9pE1iMHiR5bpp8wpy0xeQdTq+ zdAKYGkoag4xc27lIbsvsv7h0VyKpL36LcLJ/QL+Kq9PTYx1ZICmy3Jk0zuYq0wvdZYk Rhelfe1ZUA/F6W+4fi+XF6lxaxaaYVj11r6XVjlC11qUq14myfb6r2/ZYhGPtN2BR7HQ hre86I2gO3Jv/koVFq2Ul2w9r/76ZgvuJizMbvt8OiiYzyp4+ZfLLERQaELPqmGIB7oy rUl6vPZ//r9AwqgavX2Eu2MzYQ3viyVOCqnzeHt4PufTeq54xbUYWARcR+hCAHhsTSPX 5uYw== X-Forwarded-Encrypted: i=1; AFNElJ/Ox6iGZh5iOcp2npncFn2sup/dmzfnd8T7zblywrHd0vQNrm7dVpneHHkVDEHh77Ytz5wtHWf/P1mTGH8=@vger.kernel.org X-Gm-Message-State: AOJu0YwvPtoqkzgFYpF/vFuMneJQ3QeIVfhT5RvnsW8xRUa2LZxKImgF 1t0zGQEu5z6fpyFrCGHBSacP5TN2D/v61DqVM7JOVEvQXcV6CgAiVLJN/HdzqxxSYVjJ9tQ2Qgz 3KbZfpHc1G2lpES4kT3LMJJyaNw== X-Received: from ilbcl18.prod.google.com ([2002:a05:6e02:3792:b0:500:9cc9:c05d]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1913:b0:486:45c7:34c5 with SMTP id 5614622812f47-487419e6d1emr682855b6e.17.1781292553145; Fri, 12 Jun 2026 12:29:13 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:51 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-4-coltonlewis@google.com> Subject: [PATCH 03/21] 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 | 682 +------------------------------------ arch/arm64/kvm/pmu.c | 686 ++++++++++++++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 7 + 3 files changed, 694 insertions(+), 681 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index c816db5d67611..d1110febe7436 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,403 +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(vcpu->kvm, 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)) { - if (!vgic_is_v5(vcpu->kvm)) - return -ENXIO; - - /* Use the architected irq number for GICv5. */ - vcpu->arch.pmu.irq_num =3D KVM_ARMV8_PMU_GICV5_IRQ; - } - - 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; - - /* On GICv5, the PMUIRQ is architecturally mandated to be PPI 23 */ - if (vgic_is_v5(kvm) && irq !=3D KVM_ARMV8_PMU_GICV5_IRQ) - return false; - - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!kvm_arm_pmu_irq_initialized(vcpu)) - continue; - - if (irq_is_ppi(vcpu->kvm, 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(vcpu->kvm, irq) || irq_is_spi(vcpu->kvm, 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..9ad3520417413 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -8,8 +8,23 @@ #include #include =20 +#include +#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 +224,674 @@ 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(vcpu->kvm, 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)) { + if (!vgic_is_v5(vcpu->kvm)) + return -ENXIO; + + /* Use the architected irq number for GICv5. */ + vcpu->arch.pmu.irq_num =3D KVM_ARMV8_PMU_GICV5_IRQ; + } + + 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; + + /* On GICv5, the PMUIRQ is architecturally mandated to be PPI 23 */ + if (vgic_is_v5(kvm) && irq !=3D KVM_ARMV8_PMU_GICV5_IRQ) + return false; + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (!kvm_arm_pmu_irq_initialized(vcpu)) + continue; + + if (irq_is_ppi(kvm, 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(kvm, irq) || irq_is_spi(kvm, 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 ec74a58905712..520f6d926ac8c 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -56,13 +56,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); @@ -135,6 +138,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 38CAF3ACF11 for ; Fri, 12 Jun 2026 19:29:15 +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=1781292559; cv=none; b=oukuo/nKecljXi4eTVRLtAHXNzYfWuMZe+tlS+HhXZoR/3R8OKFB9oO+uqC8nyDqRDLZfRlhMYv0zOHVNn1JwIUvm6+L1VCRjqITj80vDzTqhmuYLgCo4HzvP4a6EIDrmKPmVosqwWjg9aGaypCtkzBnhPYJvwiQwiM5u5TtCBc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292559; c=relaxed/simple; bh=y194xz2pw7UR4oVxuISv6WLw/M9JjuDpUxu6e6ZXbf0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=MNKg0Ca4S7H1AS5X1KCaOvOgLvcewSLGtv2P62pyfNNJMnOk5ctNya+zbunEVcd9FHzn5hDNRVODp00r+9fb5fiv/SynGxqEpk8emkekvghkfsV7rmeB89NVSXBgmV+45rwbkSpnBi+4PPDrG41qsJg0TO7yFqzObMkQiBlgC2Q= 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=at0OOUpz; 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="at0OOUpz" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-486a24852ddso2047972b6e.1 for ; Fri, 12 Jun 2026 12:29:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292554; x=1781897354; 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=aEOgMIo61tN5cbwnLn9iRecU9xGHnAGtn6LC63IksYY=; b=at0OOUpzxqo+WNrMVoZsCPDa707Xl6mqhh8Net9xYX4O4JXyM7EftJQotFkivGRz7P meJbayUduodbJC/Q0rkRwUN3zN8OMYCz/3aoqRVA46RN2lPzsszBi7+VQjJ/xinPOFEV vrDQK5MRfLUc++pV61kigGakyCe8IkcdY2MMbP1x+pTetJ9xrmjd5jfhbN5S4Bpqkw5V s55NFtlobZ6bq7IhSHSbQXAzIu4SmfDYJqatwFd0ua6fM0IRVIZx7MBr/BzyS5PxgJHg YrNOHrtOAf/ZnVLKd8tFutI9ljZwgQryEwarTD+NARtF1gGgZaBReuRS0BFcPoUZ6mOq hGqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292554; x=1781897354; 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=aEOgMIo61tN5cbwnLn9iRecU9xGHnAGtn6LC63IksYY=; b=oleH9WrsGZvlRGlwR4bj92o5a+r+t+Tx/tqbTw+SkZIo9wPSleXe0giMKJ/c+shAAY vW+56fHlR3EGt7Za5nRiYBcZgCi2XRehwuXczhGCHIqmxSXZNL6fAqrZHbMEhkKIbxtW IxtEl7dsbgmxSVQl/zZOyir8I7KvqlFMgsXYcnvFRP1/RspVxNSHebk8vTZ1++zE6FEV QNmUGd+T5VH53bt6VreKCeG7/UH1U3eP6OvH5e/h+16hE2feqHxsLyXZl3PrJbDVShYh 7jS7VAZ/l7BOgUWWPixLoeAZJ5zBX3vAMOquDrJxkbKAG5lMp0UycwlaiQ604rtrd269 gNMw== X-Forwarded-Encrypted: i=1; AFNElJ8pQNCO/Ib6tRo8KgxwuiNW+A/W2xMukmZiQvxjCBUoOxHbT/8aSQmkUlcNHte8WMSG/19ai8NRCIeUGYQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yz7NVG7TSY+Po6VTFKqViTwXYdq72GJp68U1MfQMZ+9xptXLOKb CNXLGJ+nQfDlS9/ZdYfsdAQ2In43kzFBF6s2DudnZwhQne1G0s1HjhF9dlK2J/tbEVfl+mbERAB 0rivBSQP+FM0dMu4FuAlV8uOOmQ== X-Received: from ilbeo3-n2.prod.google.com ([2002:a05:6e02:2903:20b0:502:1132:3da5]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6809:d2:20b0:487:4624:71c3 with SMTP id 5614622812f47-48746249e89mr25208b6e.5.1781292553983; Fri, 12 Jun 2026 12:29:13 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:52 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-5-coltonlewis@google.com> Subject: [PATCH 04/21] 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 +++++++------- tools/include/perf/arm_pmuv3.h | 12 +++++++----- 3 files changed, 16 insertions(+), 14 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 diff --git a/tools/include/perf/arm_pmuv3.h b/tools/include/perf/arm_pmuv3.h index 1e397d55384ed..d045b0f3b93fe 100644 --- a/tools/include/perf/arm_pmuv3.h +++ b/tools/include/perf/arm_pmuv3.h @@ -226,12 +226,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) -/* Mask for writable bits is both P and C fields */ -#define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C) +#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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 399213ADBAD for ; Fri, 12 Jun 2026 19:29:16 +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=1781292558; cv=none; b=XL1RyS7EDn/2Yl4GdEAhlLaA5xrx/AjwbOWvW6RYkr5JxAy6Rd4VXBkMsemw1hFFT4pWeljuuetQscjBYeK/3peRPqpQWJxf/RzV89jreDGLxkiZn4z+EU1oz+lrLa5MGj4ohLZ2to0E/9KHIf8rSpDeapN8INp8X7SJM25WpMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292558; c=relaxed/simple; bh=06yjw3p+ipuRaTZAWCI5G1pBBjTnmA793yGUK4DpqJU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lEfvsBCM90qH/yqeAxnJjibHtLoJtkZl71wNg3W6p6fPROYPZuVe+1RhOLps1B2RsWnu0cyypz5+SA4XePnom6tyOL1HVVzHYP23nBEHILbVd4rOoJP3GoSHAXX/4VYt8GTQQdNWWlvX5c4gvjqMCALsQ52IhSlA/bwSUOmEXBE= 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=qt4zRkD3; 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="qt4zRkD3" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-4862ea7dc12so1800502b6e.3 for ; Fri, 12 Jun 2026 12:29:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292555; x=1781897355; 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=2M2UIoQTEUhWBAO6CcMuiUQsYo7FQeN0Fuvmz5eDMgU=; b=qt4zRkD3P79O7CMpCSgk2omvpeByXpgYK9BDRDYhwAC8+FHRR8aGBq7LgT2OwxcuoU PwPKAiPPDUmcZyakBXMBHt5xbzkAyuwYYVR388mFOdByzJkudKL3psOSscsWulusiIOC Br8SXVene6LB43AzjyN7JDdft61KRpCxslVBWRa3ZsAduCrt3p4ztm3NNtAZZiu3ipQX 3j+2fq85DkWaFF/y34kN5cpPYuATR3h8HakRWBcVgHlzN9iZDoCOspsFGGOjpslvcipm FgrQUGSgAcntqR6Txf4LGNcWg0mkC9dNcAHrz0JEh04lpgLotX6ynmO59E2pEn09Riwl B4LQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292555; x=1781897355; 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=2M2UIoQTEUhWBAO6CcMuiUQsYo7FQeN0Fuvmz5eDMgU=; b=L7M7gJOK5V5JZohbKiQR98JfMmVsT5NIMn/0MNMcezxHme3431YnDEbwCpBF4+Qe4S 2eong8vCUcVvTkPQaOoHkGD7CFOyNjGTwhRPu+8B4HT6lkLmhtw2ysFNkbBGGA1b//Lg dQvuRt9DYZXfBhHrPS7PC5iUJeNWUl+PBPba96KxyfzqROdek6rY+bTSNgLID1hicihl L5kSwhpw/qzKCyqrzZ3J7kXrgLeQb9SdJ7p6sU3VrJfAm9NtOjDE0YlqUtmGPijibtjV P+l73UyAiWRmpcQku/AahSPV+GDp1zAj5F1BY9ikP7f2t96mNV0vWL7pMzyo1Iwb/Lvw QSjw== X-Forwarded-Encrypted: i=1; AFNElJ8Zk2BaYBLJyK3qwSn1UePHlk/k5Llpi6w8BKDxveBAtSji4hYJix7+OvqeTFviumSRMrff+Q/jU/Nxo58=@vger.kernel.org X-Gm-Message-State: AOJu0YyTToPnfK0WAxTrysZoyueBuI2oBbYc189Uhb52jrdtc2n2DmEC Pw6dR2q3cAc0mf2wwAgOAx2fZrmJOU9/Bt/maT7subq9lql1JiL2yvYkZ6EEi0FrDmfdOmJXU8F eYOBnHYkrh530LT37D2hyH0YfHw== X-Received: from ilbdw10-n1.prod.google.com ([2002:a05:6e02:410a:10b0:502:1132:3db9]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1920:b0:479:e7c7:dc76 with SMTP id 5614622812f47-4872f4bc1a3mr2488426b6e.26.1781292554890; Fri, 12 Jun 2026 12:29:14 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:53 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-6-coltonlewis@google.com> Subject: [PATCH 05/21] 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 be absent from cntr_mask. Signed-off-by: Colton Lewis --- drivers/perf/arm_pmuv3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 1cceb1f614515..17bb1cfdc271c 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -1028,7 +1028,8 @@ static int armv8pmu_get_event_idx(struct pmu_hw_event= s *cpuc, =20 /* Always prefer to place a cycle counter into the cycle counter. */ if (armv8pmu_can_use_pmccntr(cpuc, event)) { - if (!test_and_set_bit(ARMV8_PMU_CYCLE_IDX, cpuc->used_mask)) + if (test_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask) && + !test_and_set_bit(ARMV8_PMU_CYCLE_IDX, cpuc->used_mask)) return ARMV8_PMU_CYCLE_IDX; else if (armv8pmu_event_is_64bit(event) && armv8pmu_event_want_user_access(event) && --=20 2.54.0.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 87F503AFD1D for ; Fri, 12 Jun 2026 19:29:17 +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=1781292561; cv=none; b=dfsaRFv+2ahltJ6TTL/2kL5PI38nSjzD+TBX2UMM9DoQIPeZzja21w4bBGkNggmZMb80SwvhUllTii+iTFtudE1ylQ99hW6FY3roeSsiK3hYfBcQo7ZbGXBjmUYZTFLgelbum0mMhHXsWaSJcFMjJJc9PwV2vJbLKCs/HB262Dc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292561; c=relaxed/simple; bh=lsvew6Pa1zH160wbrvXpJuTmj03ydii6m96IOxqlLDw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=VTnfsYUD4sLf5LEofhI17MGprKfgfuvXkPNapKZ9g4gQmn/4UyKcskVyU0gxndmO34CL2hpkpdNfqgqJFXUOKjnEaNZfMo6FhcVkvXTql8byUZxk4nZHBM1sipYmTtsQPEgDUUam88Y5NHmIAlHAmxXN3y67Jo0oblimDjiOkJI= 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=MQIMTSxO; 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="MQIMTSxO" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-48638069322so1755759b6e.1 for ; Fri, 12 Jun 2026 12:29:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292556; x=1781897356; 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=pVFHxuaku3ZzfsFio1NduM8zq/i0jn84Pu2i1DJARd0=; b=MQIMTSxOEPAXR1b693hD51AE+fLk9BzCgNbno/XYEgZJt2gHSChCAoSUX3+jfGNzBg ohW0s7076TzVo4OgYP4Fizg/ijnQRJZdwcNie14xB3BjIbLbvkUaowkd5jtgDb7RBwDm YDOwrxC5LosPAYGA/t9SM+IyC/hoPGH6WGEbdBaOUYnR9Zch9vN/PTl6PmBWBjHTs0c8 WjEWuB3UBgr2PQCA73KbquBkMZQYG8cqOG0wkVPsSPj96ie0E/+CLbyvzYy4KFDn7Xne a9ZhC1Y34GAz0j7y9qs9p28wxgtEHfYLKmPdXN7cLfpdHCh/xnFmouLvijvdT2vjHDGB XFzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292556; x=1781897356; 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=pVFHxuaku3ZzfsFio1NduM8zq/i0jn84Pu2i1DJARd0=; b=m0SJFhL7qVYkylRfKKIUGsH2wHvlJQOiy71G9c5mhv/tEILZJA0pVSxVsT0IqcuAud FNqiDmMoS6mCKMFWeF2Uxsbsj2AGXmLo+sABYcp+Ek7ZD5uhOpSwuagABdn2B94vD1Gs mGj5aRK3YdNQ2euVZDFwX27eD/1Yl6hT8e9RpTrJEI0oHNt4Y3gVG3dByndmywgFmHHU ZWsnDpPreedb49y3xOn+Vi3oLQmXRJip1mkbGHSHqxvpX4thK7qyeFBw4rb9lyftX459 UesxGpzi3z2NTOEAZjCzEQJ0vmAkh33vV/b4k4CMre7NC1FB4Y+Pe/s0bXpOtZWvG7sZ RAaw== X-Forwarded-Encrypted: i=1; AFNElJ9m6lxNe/jenic91d9o+sN/krq5HTHCl5MD5mOtJudDFxFC7a5LCWqW1dxo6ZJxk1H6ZiWvwEaydwqyLuk=@vger.kernel.org X-Gm-Message-State: AOJu0YztYifTDv5PAtOK+rgXsKBL1PyvbcobybeHRJaIvxiTjLzPqzG8 xeUMQeaoxRfzoaH/hlGZsMgTyDhUCtPhdurIxQ+cI4ruNUG11Br1L4HXa9XTTf4tcxvwsVJ4e1a XWf0YBDLELvw4lMjvHzdvNUwZjw== X-Received: from ilnc8.prod.google.com ([2002:a92:dc88:0:b0:500:1d31:d513]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1250:b0:479:d779:3544 with SMTP id 5614622812f47-4872f2e1d0fmr2673319b6e.5.1781292555927; Fri, 12 Jun 2026 12:29:15 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:54 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-7-coltonlewis@google.com> Subject: [PATCH 06/21] perf: arm_pmuv3: Allocate counter indices from high to low 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 minimize collisions between host and guest counters, allocate host counters from high to low. How the pivot HPMN is defined to partition the c= ounters gives the guest the low index counters. Doing this with index math instead of defining a for_each_set_bit_reverse macro is safe because cntr_mask is always a dense range while the host is running. Signed-off-by: Colton Lewis --- drivers/perf/arm_pmuv3.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 17bb1cfdc271c..d7a49dc0b0be6 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -953,10 +953,12 @@ static int armv8pmu_get_single_idx(struct pmu_hw_even= ts *cpuc, { int idx; =20 - for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS)= { - if (!test_and_set_bit(idx, cpuc->used_mask)) + for (idx =3D ARMV8_PMU_MAX_GENERAL_COUNTERS - 1; idx >=3D 0; idx--) { + if (test_bit(idx, cpu_pmu->cntr_mask) && + !test_and_set_bit(idx, cpuc->used_mask)) return idx; } + return -EAGAIN; } =20 @@ -969,17 +971,22 @@ static int armv8pmu_get_chain_idx(struct pmu_hw_event= s *cpuc, * Chaining requires two consecutive event counters, where * the lower idx must be even. */ - for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS)= { + for (idx =3D ARMV8_PMU_MAX_GENERAL_COUNTERS - 1; idx >=3D 0; idx--) { if (!(idx & 0x1)) continue; - if (!test_and_set_bit(idx, cpuc->used_mask)) { - /* Check if the preceding even counter is available */ - if (!test_and_set_bit(idx - 1, cpuc->used_mask)) - return idx; - /* Release the Odd counter */ - clear_bit(idx, cpuc->used_mask); + + if (test_bit(idx, cpu_pmu->cntr_mask) && + test_bit(idx - 1, cpu_pmu->cntr_mask)) { + if (!test_and_set_bit(idx, cpuc->used_mask)) { + /* Check if the preceding even counter is available */ + if (!test_and_set_bit(idx - 1, cpuc->used_mask)) + return idx; + /* Release the Odd counter */ + clear_bit(idx, cpuc->used_mask); + } } } + return -EAGAIN; } =20 --=20 2.54.0.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 16AFD3B1ED7 for ; Fri, 12 Jun 2026 19:29:18 +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=1781292561; cv=none; b=WBUzldRxCbzatAwgzF3u1TTh0KYh3fxlLnN0FZ3V3YW5GrGSjZCFPyhD9JoNTcoyB+SKvedU0XcFJ9wIQk/IEQPujQjNvQMrMUzXbXQyCDBX7wMfSI5iPPxmHXuJrUTI5ZJU/fjZQQnlOBSyPyF0RRlEr3ZQBGXdG2TQVgI9AUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292561; c=relaxed/simple; bh=wzdN449XCkh/SBa+eNDCLzt8pILiiHxYLcT1caFzKpU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=PHF4jaKiEO9JWjSGALeJSPVYe2JP4u5dYC3HweCzhFPnkEenU5UhZMX5y2DTUjBvZjlJPdnDny2Y1Yb7Owdaqazn28VGrkCUjZ0VkZIVFNS/OSmHFCdW8WRFt7pzKesxrdktls/TSU9zSxDL1PwsUEuqFADRDgHcKxnrENcyDp8= 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=BjYMMhxE; 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="BjYMMhxE" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-48679a595e2so1801372b6e.3 for ; Fri, 12 Jun 2026 12:29:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292557; x=1781897357; 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=FTKnTxX+8DMTE9NLdCA6P/+xqnJZerMbDO6oNRBMDK4=; b=BjYMMhxEbWWr0kW9/qN+4uWVTPVI/6DhYs7jTCagM4q39x/GT5q2yusp6UKiJa3zgt CY5pEBA7nZrpMng95ZhU692/+PjuTVgb++Ff+TKJfb7dgtbPU4KcWX7A/I5ij/7eHWjn IMtJAnfg+AQvfvsKvibPivTuNelfT3wxo/ws4Q4ljayNVsocabHh6e82PeBej6FzIpN3 1dnjuBZHexn/W2ZC3Is21OyelBt6BmPrN8lPw2Y7HkQy3qZKKSO5b/GslC71uddlQaDH gEdxrgkb1e6620K/HmzequLOn1GcCsvSJa2GLGayUtjZIY857TfKTJXzQt+hou4dkXns jTcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292557; x=1781897357; 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=FTKnTxX+8DMTE9NLdCA6P/+xqnJZerMbDO6oNRBMDK4=; b=ZCdttxqNMmkOvFa8F4WgcQgSwpdBJTB4UQLrKfjkD0XFuY2IQiGmgWYyz4zJBwRoRa GyBtKkbEqhegibXu0czkV3n44sCCrBiyS754F4oTbh8Uwdp2Za3tv4nqho62rWBv4SSm 3KSnKuthDVkkMeYee5KsoTxtvFNnIEvdM3+Ed5CFO/Qlce2VwQk1LtNyn267HoBMARIk Htj4idlHztIO81WdriCFauIIEulXc7c10jdT0oGc2tI0h5GtmixoW6G/PmamqJILHWWj PDygkwOVn6dha7QcOBGlYmt1aASxOOw4naBMK9RTmWY66TmP+PfGImkmtA7qYnjTbjug bqRg== X-Forwarded-Encrypted: i=1; AFNElJ+6N6yBKG43FAWrG3UbFvSWrLwTP+1Ty6zS6D2jyuE7mCpOlMxoN4f56+JDBsqulnCcfpRZ8NUbTp4SwPQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yzu9WUODbphcdW4jSGUT0ugKp6X3Phq/4AuZjWt9Z0ReRweLfPE /jEF4q6lNwFKqzfsFVXf3izRsrz6L/hHf5Y6YxyMQU+/nJdjfqqh4sYCnhIzk4R386QluBdUZlL AUaGVyzLmbq9w9/j7j0BM0mgbAA== X-Received: from jahp3-n1.prod.google.com ([2002:a05:6638:6c83:10b0:5e2:7637:d35]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1514:b0:486:37ab:e829 with SMTP id 5614622812f47-4872f65317bmr2796469b6e.41.1781292556839; Fri, 12 Jun 2026 12:29:16 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:55 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-8-coltonlewis@google.com> Subject: [PATCH 07/21] 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 partitions 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 | 10 +++- include/linux/perf/arm_pmu.h | 7 +++ 7 files changed, 121 insertions(+), 6 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 ecfede0c03486..fddcd6e6f76b2 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 59612d2f277c1..0e7b8e65c4c93 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 d7a49dc0b0be6..c187397134990 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); @@ -1317,6 +1329,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; @@ -1331,17 +1391,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_impl, 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_impl); =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_impl); + + bitmap_copy(cpu_pmu->cntr_mask, cpu_pmu->hw_cntr_impl, 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 520f6d926ac8c..f9a0823666949 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -50,8 +50,11 @@ struct arm_pmu_entry { struct arm_pmu *arm_pmu; }; =20 +extern bool armv8pmu_is_partitioned; + bool kvm_supports_guest_pmuv3(void); -#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num !=3D 0) +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); void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx,= u64 val); @@ -120,6 +123,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..2e1e7a48e05ff 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -109,6 +109,12 @@ struct arm_pmu { */ int (*map_pmuv3_event)(unsigned int eventsel); DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS); + /* + * Keep a copy at initialization of which counters the + * hardware implements to restore cntr_mask after + * modifications. + */ + DECLARE_BITMAP(hw_cntr_impl, ARMPMU_MAX_HWEVENTS); bool secure_access; /* 32-bit ARM only */ struct platform_device *plat_device; struct pmu_hw_events __percpu *hw_events; @@ -129,6 +135,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 3F6543ACA61 for ; Fri, 12 Jun 2026 19:29:19 +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=1781292563; cv=none; b=OZ0fZI3tZ6Lk4juBbHQ21/XeccroMdPvKTkFjIT7z7QeL67jL/DpFis9tiI0H5q9SwlUROTKLabU3tUu7JQeRCXUwjR/f20+OBuD1e9qzD6BgRtk+0rR1O9SEQhLUK2e3vpdaU6UZK91WVM7xZHOax7XIp65yMKDb9zp+qxJtQ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292563; c=relaxed/simple; bh=X7K3zcksnhegJLsbK2b6vaKQjSZ4FXPc60G7gGsYlBw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=RkAcv3EciPEPBZWCgaZPh6PQUcOoLvGHAKMK43MPyarJBSUnVYbFWXnlad4dT3Mu2UKEMkQu4m5T/A9OOCelyXeNxnkhEWYfvhHNXL3GpG/B242zHob9kD2cgHJPLEFcRvSuhSo5NBK7v4v4OrN0HVJrEUWW87Ly4tApLLGNHMY= 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=U47hXDQ1; 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="U47hXDQ1" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-48576b535b6so1165667b6e.2 for ; Fri, 12 Jun 2026 12:29:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292558; x=1781897358; 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=VFEHA4Hh+HbXwkTEMjUJrcBC6i0Zz455koc6wHXTBjw=; b=U47hXDQ1+ksn7OSxeH7NHxKpUU7GQzINevtcr1wYj/fr5WSC32CxV9i2e4V3/jBl74 9YFBqo0vQJU3fIlAT4TzLVHCAh5m33Gg21t8N4rAS/EhjiRhQI+IcCYT9a5IsrlaWmis et01qFKYfhEM9NhaTntEG/zY4D1aXt2HdbLDfLY4hP5FEFWm2xcasTdD4KkgxNdkwcii BamG2wprgViVoKL81J+3Y7KVuXzY4c1kJojDPpWK86GZpqx419l2D4WoT47vEkoGSpKi B000ZFz91JuY51MPLATrjvPZ5j52qNSRqJteAF5sykorDKsKS4RpvAM558pVCiMBpdvR iPCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292558; x=1781897358; 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=VFEHA4Hh+HbXwkTEMjUJrcBC6i0Zz455koc6wHXTBjw=; b=A4hzKzoihC3GJf8oSoFH3cIqYuhVOep+wAMwLf2AT806ae9yCs/lMQOJv7uo7AAP7D q7X3Lft6XkifOr8Wo+wvlf5GHSOwFNrQc19YTsO+VXzlZZ5FOrlR4wxzJ+nGzo7GDXNX 6tFqtolytFUlg9rYTGsEO3kBr+dIOrYtJqYyZXNn5tDCSm3Dafwe9Nn7TTpXhEDMqe4X 3FqDRYeGjgjsqi9uVW0dZiTBp0y2NTRbKzG9ZsN0ZRrxGwBfq5aVmKyXtDV44+jh2s6I rQnSq5uxvGv5jsG7S/aG84VrC5YgBLhz5dApcEwavMb6dJfQq9e0na2hGf5fyFhauni0 zhUw== X-Forwarded-Encrypted: i=1; AFNElJ9M/z83z6pZvLZl5fuzN0FlAEcVuiGxnj00AahN/1384pL1z7kR7XWE4+d5vctui8EeiN6Hxmo7DzbbTac=@vger.kernel.org X-Gm-Message-State: AOJu0Ywnp+JW56HsslyzqZu4iyFc9exYjAvVyx8WGPw7R4nx3mYR88IU XSg7YG/kvxDhW7I2Zok+KwcSnGewSEu81ZxurDlZGUBarOeoSvaSPvn6+v6zdTDHOd0vj6r+xeL OIfK5L1cktLtURwF3fUOUdjgeVg== X-Received: from iliy12.prod.google.com ([2002:a05:6e02:118c:b0:501:c810:dcf4]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:8613:b0:480:4024:3bb with SMTP id 5614622812f47-48741ab6e19mr461706b6e.24.1781292557698; Fri, 12 Jun 2026 12:29:17 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:56 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-9-coltonlewis@google.com> Subject: [PATCH 08/21] 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. Signed-off-by: Colton Lewis --- arch/arm/include/asm/arm_pmuv3.h | 4 +++ arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/config.c | 41 ++++++++++++++++++++++++++++--- arch/arm64/kvm/pmu-direct.c | 36 +++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 12 +++++++++ 5 files changed, 92 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index fddcd6e6f76b2..eedf58ea01b10 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 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/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 0d7a620c69ee2..9c7e9b92dfbd3 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -367,6 +367,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/kvm/config.c b/arch/arm64/kvm/config.c index 0622162b089e5..f052ec8a00309 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -1685,12 +1685,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 static void __compute_ich_hfgrtr(struct kvm_vcpu *vcpu) @@ -1727,7 +1762,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 @@ -1735,8 +1770,8 @@ 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); } =20 if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) { diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 74e40e4915416..0135989667564 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,37 @@ bool has_host_pmu_partition_support(void) return has_vhe() && system_supports_pmuv3(); } + +/** + * 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 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_pmu_is_partitioned() - Determine if KVM has a partitioned PMU + * @kvm: Pointer to kvm struct + * + * Determine if KVM has a partitioned PMU by extracting that field and + * passing it to :c:func:`pmu_is_partitioned` + * + * Return: True if the KVM PMU is partitioned, false otherwise + */ +bool kvm_pmu_is_partitioned(struct kvm *kvm) +{ + return pmu_is_partitioned(kvm->arch.arm_pmu) && + test_bit(KVM_ARCH_FLAG_PARTITION_PMU_ENABLED, &kvm->arch.flags); +} diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index f9a0823666949..36960b9e52da2 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -95,6 +95,9 @@ void kvm_vcpu_pmu_resync_el0(void); #define kvm_vcpu_has_pmu(vcpu) \ (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3)) =20 +bool pmu_is_partitioned(struct arm_pmu *pmu); +bool kvm_pmu_is_partitioned(struct kvm *kvm); + /* * 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 @@ -134,6 +137,10 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm= _vcpu *vcpu, { return 0; } +static inline bool kvm_pmu_is_partitioned(struct kvm *kvm) +{ + 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, @@ -231,6 +238,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 pmu_is_partitioned(void *pmu) +{ + return false; +} + #endif =20 #endif --=20 2.54.0.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 34D4A3B6371 for ; Fri, 12 Jun 2026 19:29:20 +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=1781292568; cv=none; b=qoCJJluUdjKB+YAB6jRHzEewtWQj200OQQT7Dic89qXYe+C0uN4y4ZhcKxdmcsO7QBuOC0XYGYxNAteA91vQoHOAbT2dKSaGGYQtlhvBKMofyMHUo+Vx0ZjfATw6cBwKlG+m8RfbketvPw9o1NrDPsLbVYunO4qQB39fceNt0m8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292568; c=relaxed/simple; bh=iDy5+j0nb9ZzT1lZTvVDNd2wmqQSBDk1yQc1yUrAGb4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lkH3Rb7Ops8ILzBhklsXHy5W6s7FlLNxV9L5ljpkO/dYatKO5K/KuHvmbcxLu1H7NnjOID55Wq6Gq9grKcadjxR2wenpAgpOCRC9Z7K3JJtGc76pW6G0Hi/Xzr+atIZWvhOEB+6h5ztp8UcIvhEwLfpI5SBiDdhkiUICUI5DogM= 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=ZadpGGYg; 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="ZadpGGYg" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-486cc29e03bso2001158b6e.3 for ; Fri, 12 Jun 2026 12:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292559; x=1781897359; 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=wX0J1ZgJ0AcfnjFoXewJ6CMfNQ+azFxPeQUJWlNBlCk=; b=ZadpGGYg18wCONiqQs/CYqwUKpPKgKxjDU3YRa/Pclwbb4qYR672OtddG13Sc9HLFr OOzTyjZ/s1sqaiyhf870zMAt1VLG8zmYYaEYxf5UfjyxD6yR3wC96n1VNLQHNmLkpw6N BOb8SBcnFkRefAR3ZSuc4QAGX6TGe1QYszZ/DBoXXzmSLGcSL0my7ELB+RuXjdH/ux4c hNdgL3uj5DAtEOOxjVnE2kM08RxlrBQ1CoCKcG6J8f+RZrCg9HEBCuBiHyjE5A/QTcXk 3hqG/C2qiA3X5v/qSXfk5AWIpjhdsme7Ir9eG/wT+kWUpr3X0RJeTguh458F+tCmLpwq 3caA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292559; x=1781897359; 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=wX0J1ZgJ0AcfnjFoXewJ6CMfNQ+azFxPeQUJWlNBlCk=; b=kXBbC+6cvPd41uitPq2UxFb9siidAE71lu82l+aukmI3XCjOaEQPRRndek0qYeZwXr PXuER3OQT8ceg2ZuvuI8L9tOkFAPU3zh2Beh7yIOv26juOa2IRHxj3+HgvQTg/9xkVAg kMQq8mBlczLNWEz0N6UXTKG8Gi38BjG5r7BidbJ4nQKvvi9IVpvalv3HREGfEGY0qk2o S6HG1mYlz82xzrGFp1stPfwOA5987cFjgvpKraLoTZS3o6JKUbmFg3G+pUXqjfUOcbj9 TazFlivDuU7bsQRxaG+FmNldQRmCX9ro2alrZw32t9BQOjhLgA9vWZhVWxgAKsySDuoV vIrg== X-Forwarded-Encrypted: i=1; AFNElJ8QPvnaCJbV7IE8UfO4l9iRCxRutKmsGr3euxUQ+s4D5C0qGcc0WfSEaSCKsGXAEDEzAmkJ5hs1LBjizfI=@vger.kernel.org X-Gm-Message-State: AOJu0YxnOTnAiihBh5U0sce5yrmT9oD0wbl+zSPjZOtsj7QIQ4hphHvz 4giiVDiy2t6T0CsjySAurzXNjs9M20w/G5fsEQTbxML6TvXt4QPZWLihchiH/40t/LtFTa3reNp GMiCyLNYpgHllJZAJKZGtRUCOcw== X-Received: from ilec10-n2.prod.google.com ([2002:a05:6e02:614a:20b0:500:2720:748]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:2516:b0:486:3537:218b with SMTP id 5614622812f47-487419e6716mr726786b6e.11.1781292558550; Fri, 12 Jun 2026 12:29:18 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:57 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-10-coltonlewis@google.com> Subject: [PATCH 09/21] 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/pmu-direct.c | 30 ++++ arch/arm64/kvm/sys_regs.c | 265 +++++++++++++++++++++++++++++------- include/kvm/arm_pmu.h | 7 + 3 files changed, 254 insertions(+), 48 deletions(-) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 0135989667564..43f04c2f33d44 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 @@ -56,3 +57,32 @@ bool kvm_pmu_is_partitioned(struct kvm *kvm) return pmu_is_partitioned(kvm->arch.arm_pmu) && test_bit(KVM_ARCH_FLAG_PARTITION_PMU_ENABLED, &kvm->arch.flags); } + +void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 val) +{ + bool reset_p =3D val & ARMV8_PMU_PMCR_P; + unsigned long mask; + int i; + + val &=3D ~ARMV8_PMU_PMCR_P; + + write_sysreg(val, pmcr_el0); + + if (reset_p) { + mask =3D kvm_pmu_implemented_counter_mask(vcpu) & ~BIT(ARMV8_PMU_CYCLE_I= DX); + + if (!vcpu_is_el2(vcpu)) + mask &=3D ~kvm_pmu_hyp_counter_mask(vcpu); + + for_each_set_bit(i, &mask, ARMV8_PMU_MAX_GENERAL_COUNTERS) + write_pmevcntrn(i, 0); + } +} + +u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu) +{ + return u64_replace_bits( + read_sysreg(pmcr_el0), + vcpu->kvm->arch.nr_pmu_counters, + ARMV8_PMU_PMCR_N); +} diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index c52873a6f91ed..94572bc52c32a 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1070,9 +1070,192 @@ static u64 reset_pmcr(struct kvm_vcpu *vcpu, const = struct sys_reg_desc *r) return __vcpu_sys_reg(vcpu, r->reg); } =20 +/** + * pmu_reg_write() - Register writes for Partitioned PMU + * @vcpu: Pointer to vcpu + * @reg: vcpu register + * @val: value to write + * @set: setting or clearing a mask + * + * Helper for sys_reg.c register accessor functions. + */ +static void pmu_reg_write(struct kvm_vcpu *vcpu, enum vcpu_sysreg reg, u64= val, bool set) +{ + unsigned long flags; + u64 mask; + int idx; + + switch (reg) { + case PMCR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + kvm_pmu_direct_pmcr_write(vcpu, val); + else + kvm_pmu_handle_pmcr(vcpu, val); + break; + case PMSELR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + write_sysreg(val, pmselr_el0); + else + __vcpu_assign_sys_reg(vcpu, reg, val); + break; + case PMEVCNTR0_EL0 ... PMCCNTR_EL0: + idx =3D reg - PMEVCNTR0_EL0; + + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) + write_sysreg(val, pmccntr_el0); + else + write_pmevcntrn(idx, val); + } else { + kvm_pmu_set_counter_value(vcpu, idx, val); + } + break; + case PMEVTYPER0_EL0 ... PMCCFILTR_EL0: + idx =3D reg - PMEVTYPER0_EL0; + + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + mask =3D kvm_pmu_evtyper_mask(vcpu->kvm); + __vcpu_assign_sys_reg(vcpu, reg, val & mask); + } else { + kvm_pmu_set_counter_event_type(vcpu, val, idx); + kvm_vcpu_pmu_restore_guest(vcpu); + } + break; + case PMCNTENSET_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (set) + write_sysreg(val, pmcntenset_el0); + else + write_sysreg(val, pmcntenclr_el0); + } else { + if (set) + /* accessing PMCNTENSET_EL0 */ + __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, |=3D, val); + else + /* accessing PMINTENCLR_EL1 */ + __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, &=3D, ~val); + + kvm_pmu_reprogram_counter_mask(vcpu, val); + } + break; + case PMINTENSET_EL1: + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (set) + write_sysreg(val, pmintenset_el1); + else + write_sysreg(val, pmintenclr_el1); + } else { + 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); + } + break; + case PMOVSSET_EL0: + local_irq_save(flags); + if (set) + /* accessing PMOVSSET_EL0 */ + __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, val); + else + /* accessing PMOVSCLR_EL0 */ + __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, &=3D, ~val); + local_irq_restore(flags); + break; + case PMUSERENR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + write_sysreg(val, pmuserenr_el0); + else + __vcpu_assign_sys_reg(vcpu, reg, val); + break; + default: + WARN_ON(1); + break; + } + +} + +/** + * pmu_reg_read() - Register reads for Partitioned PMU + * @vcpu: Pointer to vcpu + * @reg: vcpu register + * + * Helper for sys_reg.c register accessor functions. + * + * Return: value read + */ +static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum vcpu_sysreg reg) +{ + u64 val =3D 0; + int idx; + + switch (reg) { + case PMCR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + val =3D kvm_pmu_direct_pmcr_read(vcpu); + else + val =3D kvm_vcpu_read_pmcr(vcpu); + break; + case PMSELR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + val =3D read_sysreg(pmselr_el0); + else + val =3D __vcpu_sys_reg(vcpu, reg); + break; + case PMEVCNTR0_EL0 ... PMCCNTR_EL0: + idx =3D reg - PMEVCNTR0_EL0; + + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) + val =3D read_sysreg(pmccntr_el0); + else + val =3D read_pmevcntrn(idx); + } else { + val =3D kvm_pmu_get_counter_value(vcpu, idx); + } + break; + case PMEVTYPER0_EL0 ... PMCCFILTR_EL0: + val =3D __vcpu_sys_reg(vcpu, reg); + break; + case PMCNTENSET_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + val =3D read_sysreg(pmcntenset_el0); + val &=3D kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu); + } else { + val =3D __vcpu_sys_reg(vcpu, reg); + } + break; + case PMINTENSET_EL1: + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + val =3D read_sysreg(pmintenset_el1); + val &=3D kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu); + } else { + val =3D __vcpu_sys_reg(vcpu, reg); + } + break; + case PMOVSSET_EL0: + val =3D __vcpu_sys_reg(vcpu, reg); + break; + case PMUSERENR_EL0: + if (kvm_pmu_is_partitioned(vcpu->kvm)) + val =3D read_sysreg(pmuserenr_el0); + else + val =3D __vcpu_sys_reg(vcpu, reg); + break; + default: + WARN_ON(1); + break; + } + + return val; +} + 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_reg_read(vcpu, PMUSERENR_EL0); bool enabled =3D (reg & flags) || vcpu_mode_priv(vcpu); =20 if (!enabled) @@ -1111,18 +1294,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_reg_read(vcpu, PMCR_EL0); 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_reg_write(vcpu, PMCR_EL0, val, 0); } else { /* PMCR.P & PMCR.C are RAZ */ - val =3D kvm_vcpu_read_pmcr(vcpu) + val =3D pmu_reg_read(vcpu, PMCR_EL0) & ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C); p->regval =3D val; } @@ -1137,10 +1319,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_reg_write(vcpu, PMSELR_EL0, p->regval, 0); else /* return PMSELR.SEL field */ - p->regval =3D __vcpu_sys_reg(vcpu, PMSELR_EL0) + p->regval =3D pmu_reg_read(vcpu, PMSELR_EL0) & PMSELR_EL0_SEL_MASK; =20 return true; @@ -1217,6 +1399,7 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { + enum vcpu_sysreg reg; u64 idx =3D ~0UL; =20 if (r->CRn =3D=3D 9 && r->CRm =3D=3D 13) { @@ -1226,7 +1409,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_reg_read(vcpu, PMSELR_EL0)); } else if (r->Op2 =3D=3D 0) { /* PMCCNTR_EL0 */ if (pmu_access_cycle_counter_el0_disabled(vcpu)) @@ -1254,18 +1437,21 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, if (!pmu_counter_idx_valid(vcpu, idx)) return false; =20 + reg =3D PMEVCNTR0_EL0 + idx; + if (p->is_write) { if (pmu_access_el0_disabled(vcpu)) return false; =20 - kvm_pmu_set_counter_value(vcpu, idx, p->regval); + pmu_reg_write(vcpu, reg, p->regval, 0); } else { - p->regval =3D kvm_pmu_get_counter_value(vcpu, idx); + p->regval =3D pmu_reg_read(vcpu, reg); } =20 return true; } =20 + static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_param= s *p, const struct sys_reg_desc *r) { @@ -1276,7 +1462,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_reg_read(vcpu, PMSELR_EL0)); 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); @@ -1292,12 +1478,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_reg_write(vcpu, reg, p->regval, 0); + else + p->regval =3D pmu_reg_read(vcpu, reg); =20 return true; } @@ -1331,16 +1515,9 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, st= ruct 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_reg_write(vcpu, PMCNTENSET_EL0, val, r->Op2 & 0x1); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); + p->regval =3D pmu_reg_read(vcpu, PMCNTENSET_EL0); } =20 return true; @@ -1349,22 +1526,17 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, s= truct sys_reg_params *p, 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; - - 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); + val =3D p->regval & mask; + pmu_reg_write(vcpu, PMINTENSET_EL1, val, r->Op2 & 0x1); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMINTENSET_EL1); + p->regval =3D pmu_reg_read(vcpu, PMINTENSET_EL1); } =20 return true; @@ -1373,20 +1545,18 @@ static bool access_pminten(struct kvm_vcpu *vcpu, s= truct sys_reg_params *p, static bool access_pmovs(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 (pmu_access_el0_disabled(vcpu)) return false; =20 + mask =3D kvm_pmu_accessible_counter_mask(vcpu); + if (p->is_write) { - if (r->CRm & 0x2) - /* accessing PMOVSSET_EL0 */ - __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, (p->regval & mask)); - else - /* accessing PMOVSCLR_EL0 */ - __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, &=3D, ~(p->regval & mask)); + val =3D p->regval & mask; + pmu_reg_write(vcpu, PMOVSSET_EL0, val, r->CRm & 0x2); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMOVSSET_EL0); + p->regval =3D pmu_reg_read(vcpu, PMOVSSET_EL0); } =20 return true; @@ -1415,10 +1585,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_reg_write(vcpu, PMUSERENR_EL0, p->regval & ARMV8_PMU_USERENR_MASK, 0= ); } else { - p->regval =3D __vcpu_sys_reg(vcpu, PMUSERENR_EL0) + p->regval =3D pmu_reg_read(vcpu, PMUSERENR_EL0) & ARMV8_PMU_USERENR_MASK; } =20 diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 36960b9e52da2..700d5f275b557 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -97,6 +97,8 @@ void kvm_vcpu_pmu_resync_el0(void); =20 bool pmu_is_partitioned(struct arm_pmu *pmu); bool kvm_pmu_is_partitioned(struct kvm *kvm); +void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 val); +u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu); =20 /* * Updates the vcpu's view of the pmu events for this cpu. @@ -141,6 +143,11 @@ static inline bool kvm_pmu_is_partitioned(struct kvm *= kvm) { return false; } +static inline void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 va= l) {} +static inline u64 kvm_pmu_direct_pmcr_read(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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 BCA0C3B8945 for ; Fri, 12 Jun 2026 19:29:20 +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=1781292564; cv=none; b=XeAfyShkit3+6hrLuH5ac+ii+9zCBeQm35vyTrNaSmRV7TuRML9+Suk5uftc6iVgXR7Xmxh4tDSdR0ECqh1dWYCWRTOxxFHy6AvxhlC6PC8xHVO8k3uvLorvBnS/1KYKaFxTQf1wEPHKTsCONRt1F1FQ3H0Kgk3ne3YGD5jNxpw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292564; c=relaxed/simple; bh=ZG9ZKKsG5def4qAkO71pmX+aMwBP6QL7ujSsyD5d3oQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=SdXmvKZZ2DN4uo69xFD5+vwkv0mC90rdimIUpC4ryWvB/MzNXMHXlFQbAhL4Xfrmt3jnE+3UzeGoIGK6DMiC4YaoixJlyl4pghdjmq2xw/zxes+A3Lpq1y0jST0ZXhrWxtsYOa1zzbo/OgMONQCtkOcKqTiVpoRHiVObgtHjDVA= 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=BWFIUUM7; 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="BWFIUUM7" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-48661b2ef8eso1179028b6e.0 for ; Fri, 12 Jun 2026 12:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292559; x=1781897359; 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=zUfvJDWatpVt+iiRM4HzP8H2jLIkMlFqueFXiDB4Pis=; b=BWFIUUM7+Htorv1lZO9DykvRagVsFGJtMf98QyOlKI9oMDIoZtfYjipdE6oP16XHcI ExinGVD+AWBLgcYKyV4aWK8x+JKlFXkIWtDGXm8ShhyWOk+R3XrYJmE2q29un4Ma7H8w QkcWbRDKMCyn4uIToz8fvuaS/5LMRLT0G+Osztbsbpd0TaesMl3sU2Ks/+QE7AcP0g+U brLunS2fhjkqITTwVbsxzl+vJPLhaRy32tD/FxF8WGk7kaXNL92LxaWr1VZDGgqGviEP EIMfWJQw8UhSE4Cqa9oQtyNGRjOm8bWWpiO4AW7hPtuaG5+c8ow0za8zKbzdcZgSwvhW 8/oA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292559; x=1781897359; 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=zUfvJDWatpVt+iiRM4HzP8H2jLIkMlFqueFXiDB4Pis=; b=BbkHEfLC+sqhEaJaDuQh2gyQNgQGkgXUosk4lQPWa1OwTmIV9O/BML2r+MGtFhJqSx f9KE6jnITX3E4IQLL6GjUJaxZT396vucehyabIAP2eVXLfawDdebjjXP8Kjj9QMc2m+c tszs41PEoKqxkNfsS5qYMlZl1sL4Wvsl5g8l3lsJM/4Bp8zfAJv5+Zr2q0+w1bgdUPfH oBLcJqPelNVI5TUowAzuyMO1cYI+/Vc9a45zbHA1V6Bl+0t54+ZgxMyJv4ybpnkrG4O2 3vMRs+BEZd45p5B0ArwKzsUW3lM4syaAWTdMuUQRChC7Enul5PU0Ldi8Aq7N0+c22ucd duYA== X-Forwarded-Encrypted: i=1; AFNElJ8o41M7N0OkE8tydD5vXy5Rna5uiQL8zPFgzuNCebvuhyViJT/kctGDiylSWACGVN2v7dhe5kaA628kBe4=@vger.kernel.org X-Gm-Message-State: AOJu0Yzjm9fxZVLnEMy5OmKzzU3/2qiDWDtDbs8+pjW77YI/50OFCQif TzhU4mFfvRdxOoBjh3sfGTSTER/IBBDJ0JSt2c9pQx44jfgdQ0HMzToe/7NPWBGTgW1+Rp4GnW1 0PQmXFGMlsn+exhhREnPUWMFM5A== X-Received: from jabjq11.prod.google.com ([2002:a05:6638:8d8b:b0:5e2:7e1d:1f79]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1790:b0:47b:bd7b:10e5 with SMTP id 5614622812f47-4872f35387fmr2525333b6e.3.1781292559384; Fri, 12 Jun 2026 12:29:19 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:58 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-11-coltonlewis@google.com> Subject: [PATCH 10/21] 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. If partitioned, set the HPME, HPMD, and HCCD bits. If we have the ability to use Fine Grain Traps (FEAT_FGT) also, unset the TPM and TPMCR bits that trap all PMU accesses and set HPMN to the correct number of guest counters so hardware enforces the right values. Signed-off-by: Colton Lewis --- arch/arm64/kvm/debug.c | 27 ++++++++++++++++++++++++--- arch/arm64/kvm/pmu.c | 7 +++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index f4d7b12045e8f..c84321277d893 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -43,14 +43,35 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcp= u) * 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, *host_data_ptr(nr_event= _counters)); 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_pmu_is_partitioned(vcpu->kvm)) { + u8 nr_guest_cntr =3D vcpu->kvm->arch.nr_pmu_counters; + + vcpu->arch.mdcr_el2 |=3D (MDCR_EL2_HPMD | MDCR_EL2_HCCD); + + /* + * Take out the coarse grain traps if we are using + * fine grain traps and enforce counter access with + * HPMN. + */ + if (!vcpu_on_unsupported_cpu(vcpu) && + cpus_have_final_cap(ARM64_HAS_FGT) && + (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0)) { + vcpu->arch.mdcr_el2 &=3D ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR | MDCR_EL2_HPM= N); + vcpu->arch.mdcr_el2 |=3D FIELD_PREP(MDCR_EL2_HPMN, nr_guest_cntr); + } + + + } =20 /* Is the VM being debugged by userspace? */ if (vcpu->guest_debug) diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index 9ad3520417413..55cda8021400a 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -552,6 +552,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 (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. --=20 2.54.0.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 A19EA3B9956 for ; Fri, 12 Jun 2026 19:29:21 +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=1781292567; cv=none; b=Al8vKhVAU2Z5HPVgD2ZNUpINMbMSW8rivznxXP0d+QeEVzF1pvDA8j2Ow5/+1/N0T7d32+qzqZcg45ihaWKixR4K74VFJpPfcaEsST/Uoi/1y1T1oCN0VBxfK9hSxhSJmdMXtjzWTc8aQdIwM0AI63WNZbSupT+TOKDIMEXqkEQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292567; c=relaxed/simple; bh=RkEksbwbk8kbIhWES8T+6toRKKVqJf9OYkVaucjt0jU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=SdSIRIH+GWzXmKrSCNx0ORYbhHBtcClfwGXg/UZou0liwoHqYlSJxqWe4uDu+SP6S8pm4qaoh+8k2/1gPVcYz4JtlzqjEWB9xxaQvbpSAOiW10jDHNxzwVXIUc21DAg8jzCFJYn//lDUoHyXYgFl4jCIMiQKjODNQWFIgaKppt4= 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=J1BOHKkm; 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="J1BOHKkm" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-486a2a910efso2781286b6e.2 for ; Fri, 12 Jun 2026 12:29:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292560; x=1781897360; 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=mgP7OlxDmA2YIY21MfVAeQtCru4lRgU61gAmgy3l2mA=; b=J1BOHKkmNi770uBty3a1XzEWZF7/POTVFAkVc1F91oCHXkSR3XJyiDwPp3cTpn6pC0 jdjQ1KEZfN5SVS8Pre2x/ZhtDZRwtZVvR32ADuL5NE4VHrorr/JcM5GdTFZy4MV29mtG wY4jVh9mkggALRjNEy58icc54ewboxq61jdXVJ6Ido/sTewmA1li6AQlcIeKoJmtKgyK +BqgIfdPaMvvDDV0nZqGBXDrT9r+couLJm9sIlU8Rhq5Ekkehv90O1hTDDBAB73Angtj m7X25DZZFVhQM5t65b+65rfN9Ti5U/s0O3OQRM9oKaZob6PEPYkXus42GY0lCm/c5sKE /wBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292560; x=1781897360; 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=mgP7OlxDmA2YIY21MfVAeQtCru4lRgU61gAmgy3l2mA=; b=QIzkPuXRlAY6JU/kQ/DMZzmqSAO4Uj7Zz9C5G55SoVYsTnkPylDskPGPd2s7+dcsCj q5PM+X1rmb7zcWqO636TJ33Z6mS2tTIqchpR/4DlD3qRWdBsy7vecRT5mQOW/5d7DowW 5MRfzlukJs9P9X74yZhbW29Ib9SEa5Q41QjbeklaC6pxS9r7XT5cP+1q/aycKTMnTAc3 Yq1/tUPgTdBFyvZfSjeiKxzJ21wNQxiPHvBtDTO5W4mdu7MKgvntGwdedAuN/DV+rOZ8 k6C4CbMgRTrCcNIpPVKoTGfbzAV6H7j8T87Aztcggf5hrsizkmMdyXLMX4YzSRb6PPHE UyjA== X-Forwarded-Encrypted: i=1; AFNElJ/YRKcdNT5WX8sJ+yd0dlCRpjEmHGjuy1AKts7riVr3zkTA4MrF3H5IbajgPvCRjnUYbvPKGv1NdxvmB4I=@vger.kernel.org X-Gm-Message-State: AOJu0Yyuy81L6iJm5CZ1VUaubqlqyaDpB6YyBGBhiFPo+TXJ9//r2EY1 66TIEET8wVcMOknmoXsdvG179vZ8+PD+fQKPFA1a6UDwsaosxK+Ol7iRQxoNJsr+R5mx73Z5rtK UFhQYzOaXKFIk/wVj5jUO/0ViVA== X-Received: from jabb24.prod.google.com ([2002:a05:6638:3158:b0:5e2:68f4:12c7]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1486:b0:479:e9d2:26cf with SMTP id 5614622812f47-4872f378913mr3528034b6e.15.1781292560239; Fri, 12 Jun 2026 12:29:20 -0700 (PDT) Date: Fri, 12 Jun 2026 19:28:59 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-12-coltonlewis@google.com> Subject: [PATCH 11/21] 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 | 183 +++++++++++++++++++++++++++++++ include/kvm/arm_pmu.h | 16 +++ 4 files changed, 205 insertions(+) diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pm= uv3.h index eedf58ea01b10..f6031bd522718 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -235,6 +235,10 @@ static inline bool pmu_is_partitioned(struct arm_pmu *= 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 9453321ef8c67..24f63edc8b384 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -700,6 +700,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 @@ -743,6 +744,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 43f04c2f33d44..79022447cfb9a 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -86,3 +86,186 @@ u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu) vcpu->kvm->arch.nr_pmu_counters, ARMV8_PMU_PMCR_N); } + +/** + * 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 (pmu_is_partitioned(pmu)) + return GENMASK_ULL(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 (pmu_is_partitioned(pmu)) { + u64 mask =3D ARMV8_PMU_CNT_MASK_C; + + if (pmu->max_guest_counters > 0) + mask |=3D GENMASK_ULL(pmu->max_guest_counters - 1, 0); + + return mask; + } + + 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_pmu_is_partitioned(vcpu->kvm)) + 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_pmccntr(val); + else + write_pmevcntrn(i, val); + } + + 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; + unsigned long flags; + 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_pmu_is_partitioned(vcpu->kvm)) + 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_pmccntr(); + else + val =3D read_pmevcntrn(i); + + __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); + + /* Save pending guest hardware overflows. */ + local_irq_save(flags); + val =3D read_sysreg(pmovsset_el0); + __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, val & mask); + write_sysreg(val & mask, pmovsclr_el0); + local_irq_restore(flags); + + /* Stop guest counters and disable interrupts in hardware. */ + write_sysreg(mask, pmcntenclr_el0); + write_sysreg(mask, pmintenclr_el1); + + kvm_pmu_set_guest_counters(pmu, 0); + preempt_enable(); +} diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 700d5f275b557..61f8d4ed35e10 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -99,6 +99,10 @@ bool pmu_is_partitioned(struct arm_pmu *pmu); bool kvm_pmu_is_partitioned(struct kvm *kvm); void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 val); u64 kvm_pmu_direct_pmcr_read(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 /* * Updates the vcpu's view of the pmu events for this cpu. @@ -148,6 +152,8 @@ static inline u64 kvm_pmu_direct_pmcr_read(struct kvm_v= cpu *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, @@ -250,6 +256,16 @@ static inline bool 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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 D3C143BCD09 for ; Fri, 12 Jun 2026 19:29:22 +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=1781292566; cv=none; b=q5JSiHuKBsZktq0c6Mi8baPtJ3N4Bd+U5LPvOiL+m93bekQN+s0xGznND6KEJyNsCvrLDkdEGwm9Cp/0+DWIJo1lSADSkCYyK+a8Ho9g6R+m7gTKI7/+ToEfQWNX7Ml6ynPhhkEBMB2aR8KHev3rbd9a1MlQ8SzjAuqg9mMtLEk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292566; c=relaxed/simple; bh=F2dYKN9UBGMoSrmGufMBvGUIdNx8DNh3XLEYdWtgWFQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ufYxr0sd5bn2hz6+4Mi7XHvWky9P5wYcAg2hgxvqOVV1xGDAlZ2VY5/z6josHma2VHzHz/LBig3ujclDFZ5WJm557Q/rRSYwJe0JzHtmM12nMUmEueikJV5Cf4K8VnbA89fWRmGzVCSGGs3SzsQg23md4/Mq1JNqATh+S39ttWI= 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=XRqGLkwp; 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="XRqGLkwp" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-4863abb79b0so3524908b6e.1 for ; Fri, 12 Jun 2026 12:29:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292562; x=1781897362; 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=0u2rhf2u+9BtP4X7BxzDJO2DJ1V1hUQCf9r3VcGxcNw=; b=XRqGLkwp/qPVkmm3QI4+pfvx+ipGAkzVWQ9rvcH6Gb+YGthvG57vFNaKvDPdypvbo7 qq23zPCwMuC01HMbfsJVVbxhGJZcbf2mmZF5+OpB68WE40eD8YKmeM0KKIm6zzHSHNJQ 2UkkdG0YmIjKT9cCCyOljUodNltYNHCddPWz99b4aiSp4uZq5MFQ1kwBHDVZmXK4+zQK cGFYTuIEPuUQfpPXuVOLQ7olq3eKD7Lj8esMkj8yCnPwuIPvcKbMJn9mN6Cw3dppqDWJ lubaZouTWX2QwtYELO13HLl8FpabuxOtxss7/L+Lx9pLOEPtfaljzmuANFLeINPtoJzH TfVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292562; x=1781897362; 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=0u2rhf2u+9BtP4X7BxzDJO2DJ1V1hUQCf9r3VcGxcNw=; b=OhKqssNDCDlyoBErefERmXT78zp4hJbkJWY3P+W7iD/ClrbaR1xuQStnb/TGskGfpJ eAxCRPWm/8hRIpeP06cHQUQwyu/T8/GZAIE0UUI13wm/ADQaYgjoiT3U3Et2ysts0Occ xXUreAKxbCa8/+xltySsm8sgBfTh1Q1EaocdrZcLSMSWhyXTi2fPKGIdNS7bKkx8YLpE xTgQsD+RZX9yv4auDc6AGLvVGlKGtYhkt2StcPLK+iZ0oKgziPxN+aTSieypQLBz2bK1 MFB5DWSt4H+5Yh+Dv1GLEFD/DQDrSxcgUcnNjwIu2AhAFifUwCQdT0PtHjHS7UhWosyW Lm4A== X-Forwarded-Encrypted: i=1; AFNElJ/WKQMhzK1XEj/7dMo8mllzY18kzMG39s9aQtWH0dCg4V/ytHZPfvePzzVjBGV1GV2AosZfTlbj6hO2iAQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yxjn8YrhYIWV1o0uOtTFxkdC5s9aFXkUU1gXiRRFYQO9AY9YqLT +9EoS3CNANFLQAChVYUsZpW1JJPf7EBSxwAgFgSZn7IXidparWn4Pt8jGG2dzeWwrYOwpd8mgf/ oWpti1PT2164oQRcPYPNpuV2Vkg== X-Received: from jabkc1.prod.google.com ([2002:a05:6638:a501:b0:5e2:942b:e3dd]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1a07:b0:485:6029:90e7 with SMTP id 5614622812f47-4872ddea116mr2184822b6e.10.1781292561509; Fri, 12 Jun 2026 12:29:21 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:00 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-13-coltonlewis@google.com> Subject: [PATCH 12/21] 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 | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 79022447cfb9a..49f1feb5d280c 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -131,6 +131,57 @@ 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_pmccfiltr(val); + else + write_pmevtypern(i, val); + } +} + /** * kvm_pmu_load() - Load untrapped PMU registers * @vcpu: Pointer to struct kvm_vcpu @@ -158,6 +209,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 F259D3C1416 for ; Fri, 12 Jun 2026 19:29:23 +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=1781292568; cv=none; b=oNvA6SjUtla8s/QN5gbvWYZnU+RgjN4Ub8faPkDud1tki9kSLZ8EzmVVvE6bGqKrewbFxdVNe7SQ+gCXtD1o8rWbunyNKPKveZyzr1XRNxiaUfq8aL1LduNWGHndsf4Wo9EJHBRjk7Mrnh/OaIdQPFjEZtz9w1Z2XtYRD3HY0fU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292568; c=relaxed/simple; bh=Xhl4Zk+Z9G/hrZrAOpTeVT4LR9g0qIFXYGndVpodJRo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=C5iNdcDd5we32S4mLH0vTBIkkxPTSbjR/O9/nD/tvUOZhwq+ZRK6zjLkncLG5dFL1k/IOaegjwwkmReeHJk93DS2qmc/MNLXTcrIllmnNwzeOSMb1euUmxiQ0qva1a3RqFea5cHKyWjUop1dZOz2JCUPh+mB1n/ChJZ12xOFlsM= 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=v5Yrhj+E; 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="v5Yrhj+E" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-4865ab81f1fso2209805b6e.1 for ; Fri, 12 Jun 2026 12:29:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292563; x=1781897363; 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=Ld//V/f0QfaYRycD8rujhv5xi8e1YzlAhgupiZPHoEU=; b=v5Yrhj+E5Nei9NwdmdVLZ+xslN8rqCstdYcm/Io+RX7zrp1AkSF+/8Zwc4V3saNhnk i1fBRDtS0ZdN+9w5nbASUTO/4r/cniTYn/GFEsaapHxRGin30HkDRG/KB8meamJ7vXdM Tm5RqkQ8aRKxyjC+sjxz6F479Rlmk15uH+Kl5AyW/IHGyHvknW1AZVUEUckMq6BCoXyZ awR/JB3/QMNk/4nXpSqStAOV38h9MN+74o6nzfe5aBJGjuDg4gXa9BFJxJRnNW7QFwLk 2Y6/XgVvhhx5sUiSWomh6H+UFAI3E6imAqlgs/8Q2/lpRjHTMhbxy1Uidv9irBb8W/7Y nRyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292563; x=1781897363; 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=Ld//V/f0QfaYRycD8rujhv5xi8e1YzlAhgupiZPHoEU=; b=D7wvBIX8O9zAfChiNGJKbDvZJoqVh1eQMaaelJT7Z74vLsChFilxiBInUgyKy5kn72 ebMoWmbWpSdYMW7YDoMDlCzOXI8lTsLLTz5wyplvy0+2Ns4rNndi5Qr3+Sndk7vff6mZ nUXuq4U8F0MGj/PVxjBq3MvsN7SytNrnEIc1RAOznvKaVoyLXpNurnFR3YamjuSsnhIL NB0ACsBPX9ts1mV2wsqOshqX+emaqzTww398KSyjqcw1H+IdGPAcQ4L7vitoDawATMfR teFfGUJtkAi+4vTbB/B47Tfb9kE1g4iXB4p9S/RiP5GVgVdyakLR29xgR0XMxEv1XUiP UL4A== X-Forwarded-Encrypted: i=1; AFNElJ8Zi0uSOxs84tRg24tYgvMQs+wotfm5X7s5nDVRxKsjx3zNf5xnLgLsEt0QHMPg+nuwcJ3dpUiD6fIDT9A=@vger.kernel.org X-Gm-Message-State: AOJu0YyBz33Ejuk1DFMShVUDswortwq3Udm4VgNswFMgtPyu19obn5x7 sWdoRpfbO9dnD1s+Jn2zYfn6YV+iMju/ndFTq2CZBCtqlkcHZcHNAs4p0jH2Iwo1YwDJiiA0brj fevXg6jEr9GzcePdhSOCEdLeJjQ== X-Received: from jaan16-n2.prod.google.com ([2002:a05:6638:aad0:20b0:5e2:7885:115c]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:3020:b0:485:1173:2e4b with SMTP id 5614622812f47-48741c5f737mr622385b6e.37.1781292562500; Fri, 12 Jun 2026 12:29:22 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:01 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-14-coltonlewis@google.com> Subject: [PATCH 13/21] 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 | 31 ++++++++++++++++++++++++++++--- 2 files changed, 31 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 7935d5663944e..ad2fc080bacac 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,27 @@ 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; + unsigned long flags; + + local_irq_save(flags); + perf_ctx_lock(cpuctx, task_ctx); + __ctx_resched(cpuctx, task_ctx, pmu, EVENT_ALL|EVENT_CPU, update, data); + perf_ctx_unlock(cpuctx, task_ctx); + local_irq_restore(flags); +} +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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 0A8D03C769F for ; Fri, 12 Jun 2026 19:29:24 +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=1781292569; cv=none; b=fnoAlhhGcEB2H7zDNZY2zRnfkHHj73kalS1aZJke7OOMvPiBXUj7cS0zcNZ8VJYyuYws0vUxuqCOSq3p7tLjMVh4aV1/MTc3NyWYYhL6Bgliu1NMMVdg1/bJVDjIvK3ku6C4aN+EDjQKpejOruuKJhbkBoKYMS33SU9JPckosdk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292569; c=relaxed/simple; bh=hQtQ+lw4+n6HuW7+A+bm2V5K6NpNQJho0t6eEcDukX8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eI/9nL0KKKbIpjjggl3Bwt2prYiEo+TRJM7wXELOqq20mAaXtAsnJNyxsbpgBl7V1GafmLi1b9WK8QQgYdKPdAHf5DBUm46bmPvi4lVzQ6njrcEppatDRoBs2ODhNdO+s/I+D41eGan1/joM1Mv+Fj4NY3/DPNS9IOAaS/RCCM0= 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=RzuZjBZa; 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="RzuZjBZa" Received: by mail-oo1-f73.google.com with SMTP id 006d021491bc7-69dac65fd9bso1143275eaf.1 for ; Fri, 12 Jun 2026 12:29:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292564; x=1781897364; 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=U3mxqdo/FWgK9hnIRW4N9gL8LSmw3WCN8Nq0npc1NKo=; b=RzuZjBZahkQeahX2QQEgsyUEtuFJDJhSt4qWj5YnrzZeda/rNnQ0TuYqoo3YsI0Ttv Fn4+WBv2HUAgo5iiGw77NIVXEAuH9il+k4vvZtsoLpg6mJ1SOk7ez/Kd4+XnA8VfWAd0 50DL3cTH9QYG1b0GK3WWKYRroXGuubReiQyBYZMShBNRcyoVegRZaI59aJtr3KBWgHLc 26D1yfhVCjZiUCZlTGtbcASl8YsRAJRnhRTI0brGNKxL3lP7kp+05My3kGcINQYMulT7 ypVlZYQ74tJk+fa8CVKSbdMwR/3IttUHM03AMmxbFPGJBPA1QGnk9eCHmJIktvsIJb7X g0zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292564; x=1781897364; 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=U3mxqdo/FWgK9hnIRW4N9gL8LSmw3WCN8Nq0npc1NKo=; b=ZkdaKSOaUKQg4QlnYmnm1us1ypNtWd7t8HWOvXtfmEIp7SbKNV1ADne+yAWNQ9W6b+ SbmMsTFe4XLZU2mwhzUCa8z51P/9TG8QiCveNL/YS7ujwuoQmqAcw3PADhjOqOVbQ4uP Inj0dach7MzEEllnsQQZW4wWRA9ARhxMpFzqNgybTzACu30WtKm6A9rTrO1Ttkf2JXkW MHQFNq+h+B0qxaITg/i2kaDYfYOHvxUdQ5xTrQaPLcRPpmY51b5BQxbXnexNkB2DdmtW TjkFi7SdZXK9ip/GDbrxdAfYXLOzh3CaKm+5xllY4GW+Dm/lKeRCm/omZqluI2fJ0FBm 4PQQ== X-Forwarded-Encrypted: i=1; AFNElJ/TTSLdOXhncfuOqY9OOGH6Q6twbWZzaeUJwCGBGMPWsDvktJggWqEsOfXxfui5tXdDngHEdWR4eZyTXTc=@vger.kernel.org X-Gm-Message-State: AOJu0YyU7R70Syd23x5RYMRT1zV0e1RauSxrjT2EEiSkPOUMyTRmWyvZ FEBoUxMRmQWYNmiGiEo+Rx3pXgjOvD1GGLXgu9Ak3uKXl2NhWKzHYnIs2ua5o2bIDHDDOJgfqwz +GRgVQg7Y67AJOP4VTCK8lstkHQ== X-Received: from oaqy19-n2.prod.google.com ([2002:a05:6871:2d3:20b0:440:e30e:488f]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:16a7:b0:69e:2bf1:764e with SMTP id 006d021491bc7-69eec98b283mr491295eaf.44.1781292563404; Fri, 12 Jun 2026 12:29:23 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:02 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-15-coltonlewis@google.com> Subject: [PATCH 14/21] 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, 69 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 49f1feb5d280c..044f011c9c84b 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -87,6 +87,73 @@ u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu) ARMV8_PMU_PMCR_N); } =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_impl, 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_once("perf: Pinned host event squeezed out by KVM guest PMU p= artition\n"); + break; + } + } + } + } else { + /* + * Restoring to hw_cntr_impl. + * 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 @@ -209,6 +276,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) { @@ -317,7 +385,6 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) /* Stop guest counters and disable interrupts in hardware. */ write_sysreg(mask, pmcntenclr_el0); write_sysreg(mask, pmintenclr_el1); - 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 2e1e7a48e05ff..3139f80e877f7 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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 ED7213CAA5D for ; Fri, 12 Jun 2026 19:29:25 +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=1781292573; cv=none; b=SO9N0y2eFI6Tuo9LY5zoJqJ4IynERV3xn6SV+qgEehY0tR+h5JUDa6GCAqg9TsfXGvtJhX53EYLjOsNOaHc6yLzTz9AcQAEmAWyOwn9giDMWOynPau7rImmJyly/sUWzN6SIHOi4ZVD9xwP+5/ldegMICNf2lV+fcwWDkGmazM8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292573; c=relaxed/simple; bh=0to4/ihJht/FqtAHs4iUYvZvyFbsAXk6Avft6FDns1k=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=g5entftCtLZVl45d7mpgrYUT31RlkHN5IenNXwH5AsuAXJ1hTujNNTlmnDubiIhr+VIjhxc2H/y75Hx+WJvJadvRPeUhxVyfzOSRmIvVSzMafo4EmgTyJyyouiTSN/L5vVdrhLSe0XU5/8o8ugHgot50KXZoGbPaanOIQmfasYY= 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=kXPXDN8y; 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="kXPXDN8y" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-48638b48315so2390334b6e.0 for ; Fri, 12 Jun 2026 12:29:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292564; x=1781897364; 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=OeOBnz88LAlhog5jirlBqQZyuxdGojHS56ePpzSH+kM=; b=kXPXDN8yza2exFRwYz9aWNYYEDfevRvO7pTSbSt/6FZV+AxwEFJZZ7Ji+muNadm9WH 1llfgVFScGd91V6AxCuoVmK9WN2wDnk32qJLUzW5TZ5S4/YjOes+JNmaTozKyEBJd0UF AtKAiVWUhSAv4uAKw8vsDd9XW/xe4PGsEgaIWqeTmAZkgJFqdH3Aj993ibeLgKz0WbaS zgUnHQ05Hmv8WkH13K1/7Wk+4jafhnv2YZagzXXXfWpzAPQ5zTZ0JwCYuOUE3K36kTEJ 0wsFNr2WRGYZUDe4gzRurnn7UzDmSDgNgHRwqnTJZAWuxsWMaUyPvQliRnHOR5v0pbXw qQ6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292564; x=1781897364; 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=OeOBnz88LAlhog5jirlBqQZyuxdGojHS56ePpzSH+kM=; b=IQGCzECQrNZmyZik0vu/7YVwK+22RnbAG6+G4L0s06lwdUIHxzoOdfp6rkirhlZPKh r15Q+O65E8dpKoEp+I7w1JZBDMcPSF3Dv7hV15kdLjuvedDFlSB9bzPBUvMH9f9Jdbca 98QkAOatHcNbhR/kGjzQxF+Y5+2b02ltIzYfuhL5DQjevOrHLGF/UcNuP0gMh7CJ4ynw GvpBhxalibbH+QppTJO2L0jAAqjAiQ0hb7IYsOSo20rDie5u+jBWMe3PxfIpC0WELs07 YpSrdzoLv0DuVmZaK5CKEc1SFtRFCNNt/0x4mOJ3H7t3f3NlOPL844hcp9h8WLjsejtr 2T3Q== X-Forwarded-Encrypted: i=1; AFNElJ/cwX30TasSz1SBqSsoeNELVnv9/Z+tD1uCTyRC4GGwq79PtvM4afLO/ZTsLtjBky3YbVeo2vA6+ehBkrw=@vger.kernel.org X-Gm-Message-State: AOJu0YwHUBaZZoGT8WzjnGUF7IwPPZC5NDxW35BtxkL6GVMoWDRgAEnh WOx7bqKubR7xtYTqsGZRRd89OqYTAZ36BDiLLBFcN4nCvqryPtH4zWMY+14sHzX6g/WbGJkZRSE vNWahZctVGLTi6lVPRa6G4UT/fA== X-Received: from ilbea21-n1.prod.google.com ([2002:a05:6e02:4515:10b0:501:bba8:87a4]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:159c:b0:486:cab8:cb7e with SMTP id 5614622812f47-4872dd9f213mr2437196b6e.1.1781292564301; Fri, 12 Jun 2026 12:29:24 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:03 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-16-coltonlewis@google.com> Subject: [PATCH 15/21] 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 | 5 +++-- arch/arm64/kvm/pmu-direct.c | 21 +++++++++++++++++++-- arch/arm64/kvm/sys_regs.c | 29 ++++++++++++++++------------- include/kvm/arm_pmu.h | 8 ++++++++ 6 files changed, 52 insertions(+), 18 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm= _host.h index 9c7e9b92dfbd3..32573b10d9c5b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -1445,6 +1445,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 c84321277d893..ab80325e67c5c 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -35,7 +35,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) { preempt_disable(); =20 @@ -63,7 +63,8 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) * fine grain traps and enforce counter access with * HPMN. */ - if (!vcpu_on_unsupported_cpu(vcpu) && + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED && + !vcpu_on_unsupported_cpu(vcpu) && cpus_have_final_cap(ARM64_HAS_FGT) && (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0)) { vcpu->arch.mdcr_el2 &=3D ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR | MDCR_EL2_HPM= N); diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 044f011c9c84b..bb1f3dca03869 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -269,7 +269,7 @@ 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_pmu_is_partitioned(vcpu->kvm)) + if (vcpu->arch.pmu.access !=3D VCPU_PMU_ACCESS_GUEST_OWNED) return; =20 preempt_disable(); @@ -343,7 +343,7 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) * accessing the PMU anyway, so no need to bother with the * swap. */ - if (!kvm_pmu_is_partitioned(vcpu->kvm)) + if (vcpu->arch.pmu.access !=3D VCPU_PMU_ACCESS_GUEST_OWNED) return; =20 preempt_disable(); @@ -388,3 +388,20 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu) kvm_pmu_set_guest_counters(pmu, 0); preempt_enable(); } + +/** + * kvm_pmu_set_guest_owned() - Give PMU ownership to guest + * @vcpu: Pointer to vcpu struct + * + * Reconfigure the guest for physical access of PMU hardware if + * allowed. This means reconfiguring mdcr_el2. + * + */ +void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu) +{ + if (kvm_pmu_is_partitioned(vcpu->kvm) && + 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); + } +} diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 94572bc52c32a..f0eebeeb5ed96 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1085,15 +1085,17 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, en= um vcpu_sysreg reg, u64 val, u64 mask; int idx; =20 + kvm_pmu_set_guest_owned(vcpu); + switch (reg) { case PMCR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) kvm_pmu_direct_pmcr_write(vcpu, val); else kvm_pmu_handle_pmcr(vcpu, val); break; case PMSELR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) write_sysreg(val, pmselr_el0); else __vcpu_assign_sys_reg(vcpu, reg, val); @@ -1101,7 +1103,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg, u64 val, case PMEVCNTR0_EL0 ... PMCCNTR_EL0: idx =3D reg - PMEVCNTR0_EL0; =20 - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) write_sysreg(val, pmccntr_el0); else @@ -1122,7 +1124,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg, u64 val, } break; case PMCNTENSET_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { if (set) write_sysreg(val, pmcntenset_el0); else @@ -1139,7 +1141,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg, u64 val, } break; case PMINTENSET_EL1: - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { if (set) write_sysreg(val, pmintenset_el1); else @@ -1166,7 +1168,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg, u64 val, local_irq_restore(flags); break; case PMUSERENR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) write_sysreg(val, pmuserenr_el0); else __vcpu_assign_sys_reg(vcpu, reg, val); @@ -1175,7 +1177,6 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg, u64 val, WARN_ON(1); break; } - } =20 /** @@ -1192,15 +1193,17 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum= vcpu_sysreg reg) u64 val =3D 0; int idx; =20 + kvm_pmu_set_guest_owned(vcpu); + switch (reg) { case PMCR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) val =3D kvm_pmu_direct_pmcr_read(vcpu); else val =3D kvm_vcpu_read_pmcr(vcpu); break; case PMSELR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) val =3D read_sysreg(pmselr_el0); else val =3D __vcpu_sys_reg(vcpu, reg); @@ -1208,7 +1211,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum v= cpu_sysreg reg) case PMEVCNTR0_EL0 ... PMCCNTR_EL0: idx =3D reg - PMEVCNTR0_EL0; =20 - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { if (idx =3D=3D ARMV8_PMU_CYCLE_IDX) val =3D read_sysreg(pmccntr_el0); else @@ -1221,7 +1224,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum v= cpu_sysreg reg) val =3D __vcpu_sys_reg(vcpu, reg); break; case PMCNTENSET_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { val =3D read_sysreg(pmcntenset_el0); val &=3D kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu); } else { @@ -1229,7 +1232,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum v= cpu_sysreg reg) } break; case PMINTENSET_EL1: - if (kvm_pmu_is_partitioned(vcpu->kvm)) { + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) { val =3D read_sysreg(pmintenset_el1); val &=3D kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu); } else { @@ -1240,7 +1243,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum v= cpu_sysreg reg) val =3D __vcpu_sys_reg(vcpu, reg); break; case PMUSERENR_EL0: - if (kvm_pmu_is_partitioned(vcpu->kvm)) + if (kvm_pmu_get_access(vcpu) =3D=3D VCPU_PMU_ACCESS_GUEST_OWNED) val =3D read_sysreg(pmuserenr_el0); else val =3D __vcpu_sys_reg(vcpu, reg); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 61f8d4ed35e10..b77ddb94dc99b 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 @@ -43,6 +44,7 @@ struct kvm_pmu { int irq_num; bool created; bool irq_level; + enum vcpu_pmu_register_access access; }; =20 struct arm_pmu_entry { @@ -103,6 +105,9 @@ 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); +void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu); + +#define kvm_pmu_get_access(vcpu) ((vcpu)->arch.pmu.access) =20 /* * Updates the vcpu's view of the pmu events for this cpu. @@ -147,6 +152,8 @@ static inline bool kvm_pmu_is_partitioned(struct kvm *k= vm) { return false; } + +#define kvm_pmu_get_access(vcpu) (VCPU_PMU_ACCESS_FREE) static inline void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 va= l) {} static inline u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu) { @@ -154,6 +161,7 @@ static inline u64 kvm_pmu_direct_pmcr_read(struct kvm_v= cpu *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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 AC9ED3CC7FA for ; Fri, 12 Jun 2026 19:29:26 +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=1781292574; cv=none; b=EIlw/dKY4UAeckVH8Wu1xZnHBHrMW9ymER80lszssuJRILQO6biyKH3GIX4Z2jnoo+JXAvOns+eKDXCWqYIf7spWX7hjxCbCr0kznzi5Jpqik0+kfIWK9aaFH8GV6FRKFU/Gkdj0A/GEf+63svTXlIEzcdjeXLTdT0GsdGbLm44= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292574; c=relaxed/simple; bh=zr3w7K53w0L/DQ3i6wl/ImOA8tm3WN8USWt9IWrAR68=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WCnKZjD9w+P6h/lgJh3oCM8SXLGoSsSqo08dXE86lX0SAkYedRrsv1+RPXXIfCGKRif0tQ3EdgT49o5REG/lh3Ofu2aKvT5TjbVON6bbZ5p2nHwJs4YxUH/ZEduXFR8NrjcdQfJCCu4o1pHoYa43Yky7vLKlAF47qUZuIEUZRng= 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=FOg1RiQK; 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="FOg1RiQK" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7e6ed99b9b4so2712236a34.3 for ; Fri, 12 Jun 2026 12:29:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292565; x=1781897365; 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=ffaCtse8d32sUz83GD9kE1zbC59Hof3u/dSFHSfc7mE=; b=FOg1RiQKNPtE7T4zUJMeWQJojnT8H8BFcvetbRSDaLxyvCSwUNqOtJj4t+4lgjLC+1 i1d0H2EZSA08Q3+hC3L13bIZdQO5ZPJnX9PHYMvYwVK4ec5UQBjIVSSrRMQG0ByORsUu KLDwStfnpr1rLZz0963EYVsBCIh6u48NFB0cWI3yLC9g4yCN0z96MUcLFzUPDQFR7o8J yJ0aR2JUInDu7lcKTXCUkP1xN2wiE/tdew1Wa38hBfcdmstRl4cdeHcuUFskeF/iI5Mh k0Hlv2TI7he8p+k0ZBH5fuQI+5c4PO65NyU5QHVPjz7zVMa/BsM8uqGXj06npiTcrV7b 5pHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292565; x=1781897365; 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=ffaCtse8d32sUz83GD9kE1zbC59Hof3u/dSFHSfc7mE=; b=SePkhrEGpN7PYYsf81t09zMThb9l21PNYmrmMCen2Wp28Jin0HxcDR3qKX09pbY8bf W41IXABUZZgvBLVL8YqbUOe/EB9N/mhqmH721VTlfomzqd8tgpTRg9TmXw/Sb8VQ+6E1 6DY37odPK8iEx89lZbAeoidOInIp0ZkD4Y55ZLvH04kD99qDanDajXKwLN7uoCqs/EIb HmM0Ytw/Jxo8LU20t/MiNcUpoLrTJLpv9KF7lEvp3tW0vD502XKJ1wmIyrDxOnBkqa/x OThsBmV0EC2cGqgpVLziS5I0ggXLuIcvNPmYG67OP6xPRsjwy4HhsrzjY2jO+TPGj+NG 85Ug== X-Forwarded-Encrypted: i=1; AFNElJ+Raggu3xYASigh8r9bCLLklnOALVAA/SMSifat2shE3U3COh4PNq+4ybiHa+MY8I0nAc6Dbyx6gvKoOEA=@vger.kernel.org X-Gm-Message-State: AOJu0YzqSv6LkQ/WSPKoJG3H+SgmCMW3bNEBCgfpg09t99bCeOIr92q7 kw+8ErJkPm867Ufq+ZsAgE43hWEr+Bh/f5ncJPxCX1aet6by7mHskGSXGLl5wuslwuxka/72tiX K+xSK+XjKd4DcRZW/ZTEuvRE1Fg== X-Received: from ilbcl18.prod.google.com ([2002:a05:6e02:3792:b0:4fa:269e:802]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1b2a:b0:486:79ad:970e with SMTP id 5614622812f47-4872f389689mr2647217b6e.13.1781292565256; Fri, 12 Jun 2026 12:29:25 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:04 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-17-coltonlewis@google.com> Subject: [PATCH 16/21] 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 update 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 f6031bd522718..896fc5d6add0c 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 bb1f3dca03869..64f40cfb31012 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -405,3 +405,25 @@ void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu) kvm_arm_setup_mdcr_el2(vcpu); } } + +/** + * 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 c187397134990..6ab15a5209608 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 (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 b77ddb94dc99b..25163a689ae80 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -106,6 +106,7 @@ 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); void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu); +void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64 pmovsr); =20 #define kvm_pmu_get_access(vcpu) ((vcpu)->arch.pmu.access) =20 @@ -274,6 +275,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 880893CF68B for ; Fri, 12 Jun 2026 19:29:27 +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=1781292573; cv=none; b=gfJJu1Cv7O4yVG/dCbsGyUu6z6joCPF+kKSwod5/idvW3v2abSj+/MtAh2h4OBarMgBFUjuRCorgd2mBst2zSVUTDFK2KNi09xXzKxpwjdXsutACndV774GBEc/e0t9dFIf+fOY0mS2xUzF/mvsfXnEkXGmjqypOs+gIuHZnYgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292573; c=relaxed/simple; bh=TjYRd0nJW/jKNkkVM+Lek1arEtrbddhEOegCE5t4QhE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=RPxOUCSejq5RiSXUCbHy8OqXg6AaI8s836kd6Q7Mg2XXaTRZGyjoYDJCe6Ij1kZXNKrkA5UzurUIcEScNCoy3upaNwxMSpXTEvccbCsXFEqoSS5+Ti6P5DKU1N5gNeH7SbJXg2nc52MmcWKe3QFSP+4nbta/TQ2Yt91ys6ccELw= 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=m7ppru7Z; 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="m7ppru7Z" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-486e64c8edeso1334174b6e.2 for ; Fri, 12 Jun 2026 12:29:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292566; x=1781897366; 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=mefc4hk7l8V5q1+Ri7ebgTL5CsdX634wsG3um/9LWKo=; b=m7ppru7Zl3Qp242F/KSsbNc3LmbhrqzGkg0gUi9pl/7MS6jEKcLJ+W4ftQ80h4FvAz vmN3+sOgUIdxdrXutzrFLE+jTdcFyGLIMJQ+y049VZ+kYXrujDSXWavP+lCnJdyCNaHF EU5Dceadf1StqfR8uZNvIP1qwhZQKUyer3Ag3zNKyx6ITNubeFCOOZCYsy79MuA33VeV XId3ElqGboZdhW1A3Xd6fF80k/frqBr65lG4F75IKdM2zHg5efwIPA1BWTEbxYM9v3g0 ZP5rGJjA5Yoy4k8+OLTWczfErCeITdMLZkvKG8HCCGX6XH4ydzsjqrlzhG/fW8bcDc/Z qZpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292566; x=1781897366; 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=mefc4hk7l8V5q1+Ri7ebgTL5CsdX634wsG3um/9LWKo=; b=K5kD2y6MNkZXm2nHnG19/y/BQybwBgLYUSbQujlWC07a1Sow9J24mzE8JCbPwRfq1F 7Z+15FJnt24lDXBFPU528ojeHPQdF2ugvPEVTck8jjA+owjOlVyTTS4SXz06WWIDwptm Npv/LnmQHq2aoymbTQTE505Z24DPglYMcDxDQwSikWDdRo3SO0e9FAMYqtBqyPounYxy aZpkBqHusopcSEnN/OCZnHjyNnJVvBx0qKccjE9Z01EYinAI6IuDqiV/ASpitbED3wTP PhCzIKB0/NmuaQdBA58mv/WENbD+foICqvCQMhBxR7hdxDXuc8Fbj2VYaz43BQiOwzF4 +7LQ== X-Forwarded-Encrypted: i=1; AFNElJ+pBVRHRc3Y8dw38UyFTQwDjehQrZAhGErTnycc3yR6gfpXfy3vylDOBijWMMlqfbNsjtcy2UAxeBPbv8w=@vger.kernel.org X-Gm-Message-State: AOJu0YwB8Xs4Wvdv7hrSx78mIU0Aix3k94/v9EwcMD5TloSmcIZ9+8fI +EXbkCkF4Xj9EBByfYcJ4OynYnoo2vr7zyXYneG37sq/Q49osQGPoOtgIONx13LKuTNp7L6+CMY ThTICbJbRkhHX0Nhq14h7AR7BKQ== X-Received: from iloo20-n1.prod.google.com ([2002:a05:6e02:6114:10b0:4fe:69b2:404b]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:3a15:b0:485:d6e7:9dbf with SMTP id 5614622812f47-4872f11d549mr2509778b6e.0.1781292566143; Fri, 12 Jun 2026 12:29:26 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:05 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-18-coltonlewis@google.com> Subject: [PATCH 17/21] 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 | 48 +++++++++++++++++++++++++++++++++++++ arch/arm64/kvm/pmu-emul.c | 4 ++-- arch/arm64/kvm/pmu.c | 6 ++++- include/kvm/arm_pmu.h | 2 ++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c index 64f40cfb31012..0062d1d8e1999 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -426,4 +426,52 @@ void kvm_pmu_handle_guest_irq(struct arm_pmu *pmu, u64= pmovsr) return; =20 __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=3D, govf); + + if (kvm_pmu_part_overflow_status(vcpu)) { + kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); + + if (!in_nmi()) + kvm_vcpu_kick(vcpu); + else + irq_work_queue(&vcpu->arch.pmu.overflow_work); + } +} + +/** + * 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; + + pmu =3D vcpu->kvm->arch.arm_pmu; + mask =3D kvm_pmu_guest_counter_mask(pmu); + + if (vcpu->arch.pmu.access =3D=3D VCPU_PMU_ACCESS_FREE) { + pmovs =3D __vcpu_sys_reg(vcpu, PMOVSSET_EL0); + pmint =3D __vcpu_sys_reg(vcpu, PMINTENSET_EL1); + pmcr =3D __vcpu_sys_reg(vcpu, PMCR_EL0); + + if ((pmcr & ARMV8_PMU_PMCR_E) && (mask & pmovs & pmint)) + kvm_pmu_set_guest_owned(vcpu); + else + return false; + } + + 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 d1110febe7436..ebc68090bdb26 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 55cda8021400a..f5ee18b4dfae7 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -409,7 +409,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_pmu_is_partitioned(vcpu->kvm)) + 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 25163a689ae80..f72d080ee7ba2 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -93,6 +93,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 D4D6F3AE1AF for ; Fri, 12 Jun 2026 19:29:28 +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=1781292575; cv=none; b=Y3WgNidlQuMZmREXBaIpIYVYipb7JKG92Q+fs3KdibYb9q1v4HJ5cqP1jpoAd9qmJ/u123exxAWStuWec0Nv5Rl2SwBFVWgdzyn1nwWFg3VHw/hnQ6OuB7VkeFJnQDYsYVLVKl4FbfiZytYpJHzDOFaG3LhEhA4N5GSD3q+yVXU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292575; c=relaxed/simple; bh=Z169fBUkCIg9BiMlDMCUBmqUN7mLkV31cpse2h+VzWs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KseUXBbs4e1+NT1zL9kJvR4GZ5xxX6BS0q8Z/AgTOv/Di5pHuT1z/4eA1RnOPEWFE+515eGH12Wc3Mw2nk5F7tdGCERrh46RgJ8ZGs40cqexefcqzF0EeoJIhnaV3ZssOyX+ajTRdCD7Xy5CAWZYa8gBEjZRZCscsXyPFoygVq8= 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=tE7f40Jj; 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="tE7f40Jj" Received: by mail-ot1-f73.google.com with SMTP id 46e09a7af769-7e6d9be94a2so1040938a34.1 for ; Fri, 12 Jun 2026 12:29:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292567; x=1781897367; 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=xMUfkA0Qvrb7iW5rl1K95T0ct8KyLE0h2lxpS+ugXRA=; b=tE7f40JjN40RurEeRB6Kit/fgsxYPmcAUSJG60ZkNuqgEOsCNPjeyxz0a6mpAL4g8T ivIvSnh5fM+XLNjojUcRKAsJuGtqEnz8+fr2599fiiFYmiZ4nPMeoXtnKgJXepKX0qfy l9SbHVzRysBCok7K1zi6yTAEqfaUfCiKeY2HiOx1te+VB5RmcRGizd3qYw44DNrl+4Ey 4lyvT3xuMfB3ehL783PM1BCaT014eyWm1L9CMp+RrW6A9jqcNyIQbDmrywfDyMxMofPL jxLntPNGJkj/wSllkCdXgkwXXwLTc155IWLXdMXDVRmbstBB3xBbrgSwXTPsSMS96OhJ v6RA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292567; x=1781897367; 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=xMUfkA0Qvrb7iW5rl1K95T0ct8KyLE0h2lxpS+ugXRA=; b=aKVB/ntfqdwEBq3WK/2dQtpeGqmQB8MaSps8IXj71qWTWr2VNMJrYWpcgxNhPJZyLt o1cJS89FeJXjF3V+sd2PIZA8RvC6PjknJGEdRLBHtXOd1Qg+iAHTMI2nMUKhFZEwyxJq o1CSLDFQEnqTb5CBZHS2l+rmxT2fPg4vhpdUmF8RuvMA6Fbt6zCO2mptG7yiK4d3ILNM R7USIRR9EeyejZmvvsdA1IR7q35Opiz7S+vC6xHm+5K5dRau6fEGeCawyRdWgFgHvoIb 2oB4WNWb9i3qWT5svZeqX9PweR2oBm3KyZxiYq5woM/ABjkJj1zx3i9sDUnoM46bkNt/ TRIg== X-Forwarded-Encrypted: i=1; AFNElJ+SLWahWyLL/MfWZ0qChymSc5SvyXi5RQkc+d4Xj9DXJh625UCPLcye74eqtk/VCcqSV033rkf4F3vEjgQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxtnT+MGCwWsk44YNjj5LST6YJjChMabIGeVCYBgi/b9J+dUB6c 6E8qJW7EV/AHCT4HhOl022hxs7uWoKdtfaE7+ZknzEHtYmLjyA2E0Xe1FP8tZJ/bD57v2Nuz0aV BzAPxwQYemNfkhYBAYSxHbLaPGg== X-Received: from ilbdx1-n1.prod.google.com ([2002:a05:6e02:4201:10b0:4fa:1e84:4047]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:170f:b0:485:15bd:60e8 with SMTP id 5614622812f47-48741b51368mr655188b6e.35.1781292567145; Fri, 12 Jun 2026 12:29:27 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:06 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-19-coltonlewis@google.com> Subject: [PATCH 18/21] 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/uapi/asm/kvm.h | 2 ++ arch/arm64/kvm/pmu-direct.c | 30 ++++++++++++++++++++++++++++++ arch/arm64/kvm/pmu.c | 23 +++++++++++++++++++++++ include/kvm/arm_pmu.h | 9 +++++++++ 4 files changed, 64 insertions(+) diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/as= m/kvm.h index 1c13bfa2d38aa..7f57b8c132925 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -437,6 +437,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 0062d1d8e1999..2d2294b78ebe0 100644 --- a/arch/arm64/kvm/pmu-direct.c +++ b/arch/arm64/kvm/pmu-direct.c @@ -24,6 +24,36 @@ bool has_host_pmu_partition_support(void) system_supports_pmuv3(); } =20 + +/** + * 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); +} + /** * pmu_is_partitioned() - Determine if given PMU is partitioned * @pmu: Pointer to arm_pmu struct diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index f5ee18b4dfae7..4e15948ac2565 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -769,6 +769,28 @@ 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; + u32 val; + + if (get_user(val, uaddr)) + return -EFAULT; + + if (!has_kvm_pmu_partition_support()) + return -EPERM; + + if (kvm_vm_has_ran_once(kvm)) + return -EBUSY; + + kvm_pmu_partition_enable(kvm, val); + if (val) { + unsigned int max_counters =3D kvm_arm_pmu_get_max_counters(kvm); + + if (kvm->arch.nr_pmu_counters > max_counters) + kvm_arm_set_nr_counters(kvm, max_counters); + } + return 0; + } case KVM_ARM_VCPU_PMU_V3_INIT: return kvm_arm_pmu_v3_init(vcpu); } @@ -808,6 +830,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 f72d080ee7ba2..6a5572994b7fa 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -99,6 +99,8 @@ bool kvm_pmu_part_overflow_status(struct kvm_vcpu *vcpu); #define kvm_vcpu_has_pmu(vcpu) \ (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3)) =20 +bool has_kvm_pmu_partition_support(void); +void kvm_pmu_partition_enable(struct kvm *kvm, bool enable); bool pmu_is_partitioned(struct arm_pmu *pmu); bool kvm_pmu_is_partitioned(struct kvm *kvm); void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 val); @@ -279,6 +281,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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 46D663D25B2 for ; Fri, 12 Jun 2026 19:29:29 +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=1781292577; cv=none; b=dmN6IH9THb+SH0NyOjNfQisvpNsTByTcIyiEi/3xNsACwNHp6WiFsGpi8L6skcKPTIuYaEGXJsPdoOLDFEROTF3huTKcWSuTJxj5K9eSrsGqZleDNGcTR6h1FUJIbr1IJW9KGhDlgu3ykL0IoULs93tthYOZGvwapnY/O3nAsnI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292577; c=relaxed/simple; bh=HBuFwboneQNMp7qglhgXk7alnCfDwtLSxYiguum4PxI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XUxld67GVu22LBoNCAB5WxagW52yt2H/3ueZleGPbTXh0LJFPHApP17Rik77VFJASxKC2MGeAuXpjVhMUl5fBvsctSlWFCNQgIalD7C/z7l6if+w/QD46EXoC78JSCc/MrhdlMI/uVkV0BSiOKqtsZ6dGOIpquPS0POy7Gx7lGc= 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=pr/aGln3; 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="pr/aGln3" Received: by mail-oi1-f201.google.com with SMTP id 5614622812f47-486417cc069so1695041b6e.0 for ; Fri, 12 Jun 2026 12:29:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292568; x=1781897368; 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=lI2FhZeVSW/rDbtlOOBPVMTchx/8Z69t0suel1yDoGI=; b=pr/aGln3J9K7mB7pFQ7vKNt9N+5/LTescX762MEq40KZ1XOAC30WqI5a0vJXHE+DfU xdB68KTUPAAMR7uVW+JgMPpz4ZP0yWKhC16N/HYehe20mq+vfcZTRjBsF+KTONi4LKZZ B8vcrXsCdI8im2fICAcFWli0usmHnyg8/ELYuxJnfo8pzpDpqD24ebc27fd/sDxC2D7x oEZerZ33KuqZQxkDPXJXf8esM4OTDSuq2aHsX3pN0aKd2hUFdtpY1h5u9m3yJrUknuUL 8J6PU323xrNVo/dZz0xLqm8RbjzHFrD3P3xBh9iE086T1kjX37MiZXPaaliDY/Cd6+kK u2+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292568; x=1781897368; 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=lI2FhZeVSW/rDbtlOOBPVMTchx/8Z69t0suel1yDoGI=; b=PJjl596gmkofAzZaeSFtgcnfrjHmdLCeR1tLBM096QxCBQMtHsPaid8YIGomOjK68x 0ZsvWCgiVZNvuDIJRByD5gQM7y+AwVyRgDYFKmKSi7aeI/aAYalgwQRwnltUHhNLVbUk UhEoggFDgZLjVLHlre/b5X8I2Hop0BSvuIAPUkWxydYZtgOIiubNqVSRa2pC/gySw1ZB BR2Vha4YwMyuWT0OZzysj0eiGj4chtbUayoCppSVab7yk24RdxF6X+goi8jsQ+bRLeES rKI5NJ9P5ZF/PF0SIhZJY+KmWKiMRlc667ufNspFNzSHh3iESP5tE7fszRsHVn0FnTbl UXjg== X-Forwarded-Encrypted: i=1; AFNElJ8KPpH61QwJLsFK4mPqVNS+7hr+HWrxJiS01xSRDKIE19sjoOPVLvq2vtf2inMnG/D1SbDFzWlTcD8o1g4=@vger.kernel.org X-Gm-Message-State: AOJu0YweDH2q5K1sDBlJjvpETr9y2r9HjSgKUbsrBM6wOjpIeR+1AEzd 5zTB8uDZeSj5/Lvg7+AZOsiF+fpqIcp/rNMv3Xw7Ze1T/fsKcHx26y7PtGmuQcjVL3BRBSXF3jW BRR70AIK3bp6LpbcQBmF3//engQ== X-Received: from iljq18.prod.google.com ([2002:a05:6e02:1072:b0:500:26db:d9ff]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1b2a:b0:485:467f:a306 with SMTP id 5614622812f47-4872f352d35mr2615199b6e.4.1781292567951; Fri, 12 Jun 2026 12:29:27 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:07 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-20-coltonlewis@google.com> Subject: [PATCH 19/21] 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 | 2 ++ 2 files changed, 3 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 9118a5a51b89f..fa7a2746b1c13 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..5534248c663f7 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/find_bit.c @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "../../../../lib/find_bit.c" --=20 2.54.0.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 1AB7C3D3488 for ; Fri, 12 Jun 2026 19:29:30 +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=1781292577; cv=none; b=NKjW7YmKO3lezD/qR+tczqlCFRRbvfH7d8XiTT6gNXjQTjrbsAQ+baGxeKpRfX+MPKv6J4kr4AA97k1xYlpPYp0K+yNdJDg2PAtZQQk/UCzTnPwN+81sIUoZFP3CGB5QPTJAn5yHv2x+Y+casJnbaPQ9sUfzBplsoysirf0YWgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292577; c=relaxed/simple; bh=2hsDb6kpQESq+j2MfAkIBSgYrHzAySzBzaX39iKookI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Rm2jRsYA/XJsd6xmXcYJ4wXRCoOzgUHjEsmhRBLiY2TEL2gm9+Yvl9hjzWCnQGCEpMilXFRIOnhZSGqQdr/7CJLMFHhhe7Sw8bLRRcJkQpP7ARklUOVjQvv761S0puf31iXHJlRuOi3skHz5+7g4w6AulmCc4PcxSAofdgToSWk= 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=PA/NXo5R; 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="PA/NXo5R" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-486e64c8edeso1334213b6e.2 for ; Fri, 12 Jun 2026 12:29:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292569; x=1781897369; 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=zoa2MBSo9kga5/tD04xH7yC0HmQp8XOYPJ9Cv6PxsrI=; b=PA/NXo5RY6A3gt4qv2MYtAbPpJlZHKKP6idqhe7AnlwwVjG+LfcAuuSDfA1iOnTlo4 A48rdgp3r1Yb4SsSRMEYEVjILly5Q0NxEi5HlqeEhSLemtBeD5ksxT4OT4sINu5TfIlB 8nBlOxYdJstlv/IO+5vlpWf2XLgvjLwY7732/PLd9zAquXxbzBI43HkR9nJdUWHumDlv bN036LxqExjG0jh8h+xPSmatM75bppRSk1KhAEOtC2g/tUg4URcR86rChgKWn5vxG7MQ zlUH/rG2TyQ0j1Bt/nnBcJ0WQrz+aOOXc/AXmOQKAvCSWnTvAIk2HOPYQWmyQ0ExmVWi uQKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292569; x=1781897369; 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=zoa2MBSo9kga5/tD04xH7yC0HmQp8XOYPJ9Cv6PxsrI=; b=F9uBHUa7QZzEoBsjieCQCVJ2KrizTlVmvlsh2x6fCs86fYoT8zdFMJ+klQ82Yu0K73 vrtUSB0aFA1bOU7+qyoz6Fz8PjPcZZwdymwzfZ9Xhrb0vrk5XFYGrrXwyYE5kB0wiqRU ziJW61S5yFdIVrzBJsT4jd8cAtMDgGdffR9HzkvftWcLUOJz72TcMa5hwOAh2v85nvXk 5qZoCP819GGFISA6leUvAUaKSInjxqBZDY74vR8nsZGOMgse8GDHrEkN/vUyZq+Cv52s 5OMqAt8/+78Ygue+hRNUOjmhGg1BDvdBI+FrmtZ688SytleFfjXl47IM1dHMMKCf69wi +EnQ== X-Forwarded-Encrypted: i=1; AFNElJ+GvOxTb2aoRu6atD5g7zLxsN8a3vIn3gNbI4TSnYozHPAESLOYrE1UhSXV7EWfdlRuCGx5N40q/El623g=@vger.kernel.org X-Gm-Message-State: AOJu0YyjBo8oSroXtgX+xGAcJq7hs1nlrl8RiXhJaF2URW5ZNrgEk4IN zJH/+MF52bcBEDcfc8hsdIJNlCwFtDDfp8Rn1YHrdWVBp1Fx3eUNekyWO8O4sxntiRe80+x45+X Yp7MuJh33Y6gh9SAYPTDhsVKfQQ== X-Received: from iloo16-n2.prod.google.com ([2002:a05:6e02:6110:20b0:500:79fa:9c92]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1707:b0:486:4892:d55c with SMTP id 5614622812f47-4872f3c6289mr2573236b6e.11.1781292568836; Fri, 12 Jun 2026 12:29:28 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:08 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-21-coltonlewis@google.com> Subject: [PATCH 20/21] 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 22223395969e0..9be6034335283 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(u64 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; u8 pmuver, ec; @@ -409,6 +420,13 @@ static void create_vpmu_vm(void *guest_code) .attr =3D KVM_ARM_VCPU_PMU_V3_IRQ, .addr =3D (u64)&irq, }; + u32 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, u64 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(u64 pmcr_n) +static void run_access_test(u64 pmcr_n, enum pmu_impl impl) { u64 sp; struct kvm_vcpu *vcpu; @@ -497,7 +525,7 @@ static void run_access_test(u64 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(u64 pmcr_n) +static void run_pmregs_validity_test(u64 pmcr_n, enum pmu_impl impl) { int i; struct kvm_vcpu *vcpu; u64 set_reg_id, clr_reg_id, reg_val; u64 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(u64 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(u64 pmcr_n) +static void run_error_test(u64 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(u64 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 u64 get_pmcr_n_limit(void) +static u64 get_pmcr_n_limit(enum pmu_impl impl) { u64 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) { u64 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.1136.gdb2ca164c4-goog From nobody Sun Jun 14 01:35:13 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 987BF3D5C3E for ; Fri, 12 Jun 2026 19:29:32 +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=1781292577; cv=none; b=FOOEz6B/q9PboIqoEFPjP6xgfPmlwDQh0H6dOQXKcOiQ/t8C/7ZQ1nlZgofKbK8K6GaXtMow0Eyt3Cec4iQVc7Gse1hB97Fz9wvGkyBmBqJnjohhbK/pKl2MLo5U5aq6HzlojNZ2GKfzl57fbpKGH2JEef7JsFehbFCnF5OSGOQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781292577; c=relaxed/simple; bh=PiE+dt9fYAPOr4a0xodnENwZbbQVLSZKLUreY8s1sdM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=m/RvBMZ2qjYIWsr4LO/dF/q1dxIpogJFt7WzZb+SLt3jhMBB0MSGOB60mrUdB1PtsOe9EJSv5vouQGDTX94oUi0HGiJEDAFeBhoMVGTx47H5tqEjRq8ndETli3n9/dJe7VolqLWvAZH2n7qUSikgzOaFxVKwGergzNoOx0fRBHM= 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=SW5dC8lq; 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="SW5dC8lq" Received: by mail-oi1-f202.google.com with SMTP id 5614622812f47-48687e7f161so2129515b6e.2 for ; Fri, 12 Jun 2026 12:29:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1781292571; x=1781897371; 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=/bPxt/n97qTMkfypzesmceZlZb8m5faMbDtZZOrJQN8=; b=SW5dC8lqz/UBlm3Lu7X1m0z/71yQLgEtRYPKMZK1cpEuwZZsSFK4EQIBlpa6Wqsm3l buBjy16sghBOemeRpFhO3Re4p7rzssCdeBIRUinGWsHbzXgCJdPKsRBTVs1UnXQbNkRB fRzHBzjtH5JffmDvh8cX1POmrgryinwUfSMs0LsiNBL1XCq/XOd33srMvnjZW9z6LYt6 iyPLmFEIyOOwLBppPPcKI6cPEBD499FrgrkGOY9dyfqyUBMC8KFI6Wk+XM4OeNaGCuzD 1+alWiegqgXIEsrE62ZsnQ/YB3c0ByPZ6Ck5Ax9wj1rJxOoEgPR9+BExzGYZoLG+a/4R s+Ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781292571; x=1781897371; 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=/bPxt/n97qTMkfypzesmceZlZb8m5faMbDtZZOrJQN8=; b=g8of+8azdrLT0223QAXUQdgTLR5LuomAy7Dy3gO6222l0AD1qm4xDAfxUDTD+kkhyB ghvsVgnj3o4yyMncwYrVZ1YVYCmBaky5W98LQQlsg8P/v9QOPnklrpxbfECum7Umh5ZJ hUu/+HW+oH0TVW9589XoMe4dsMtxxRh4x7Em3hvw1MN3i6Zsgv6TeHEIVlMLx6aKE4BK oBszYNA2UXNaJjGtNwVSGyRj8nTDeE9vxziW8COsCaMjbHUPC2j+njLj+m9G9UyE/S5y pktfBo5ABkXMe7CykWfZiHVBqpLCU1Bg/OsjEE4bP5+5ttmoqBlUrznMj1zMCC9jTuQS oGHA== X-Forwarded-Encrypted: i=1; AFNElJ8g7q8gbI4QNNwSIlywq+bhl9/tR73LR8uPqnaM4R9dSTI+5x+SfK5WWSqiKjaPNa5pKEtH2JThLfi4PB8=@vger.kernel.org X-Gm-Message-State: AOJu0YynUKGOrn3QYRNSrA+7KJCsMVwa3muSp0h8B3YoDcHdYp29kumr EVzEPedvKs/fNfwf46gyuX9Kt6yYW4qV4xIs4nASI9RCQNt3Qxydtq3z8Pw0u9v/uQ5zRvMZAmX 6KFnCsQoqWz3MTCPPqd0pFYo20g== X-Received: from ilbdx1-n1.prod.google.com ([2002:a05:6e02:4201:10b0:4fa:1e84:4047]) (user=coltonlewis job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1308:b0:482:4dbd:4fde with SMTP id 5614622812f47-48741a3b47dmr672091b6e.19.1781292571289; Fri, 12 Jun 2026 12:29:31 -0700 (PDT) Date: Fri, 12 Jun 2026 19:29:09 +0000 In-Reply-To: <20260612192909.1153907-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: <20260612192909.1153907-1-coltonlewis@google.com> X-Mailer: git-send-email 2.54.0.1136.gdb2ca164c4-goog Message-ID: <20260612192909.1153907-22-coltonlewis@google.com> Subject: [PATCH 21/21] 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 9be6034335283..e8c3856df77b7 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 { u64 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.1136.gdb2ca164c4-goog