From: Colton Lewis <coltonlewis@google.com>
To: kvm@vger.kernel.org, kvmarm@lists.linux.dev,
linux-arm-kernel@lists.infradead.org
Cc: Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>,
Oliver Upton <oliver.upton@linux.dev>,
Joey Gouly <joey.gouly@arm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Fuad Tabba <fuad.tabba@linux.dev>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>,
James Clark <james.clark@linaro.org>,
Robin Murphy <robin.murphy@arm.com>,
Zide Chen <zide.chen@intel.com>,
Alexandru Elisei <alexandru.elisei@arm.com>,
Ganapatrao Kulkarni <gankulkarni@os.amperecomputing.com>,
Mingwei Zhang <mizhang@google.com>,
Jonathan Corbet <corbet@lwn.net>,
Russell King <linux@armlinux.org.uk>,
Shuah Khan <shuah@kernel.org>,
linux-perf-users@vger.kernel.org,
linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org,
Colton Lewis <coltonlewis@google.com>
Subject: [PATCH v9 18/22] perf: arm_pmuv3: Handle IRQs for Partitioned PMU guest counters
Date: Thu, 24 Sep 2026 17:29:24 +0000 [thread overview]
Message-ID: <20260924172928.2110956-19-coltonlewis@google.com> (raw)
In-Reply-To: <20260924172928.2110956-1-coltonlewis@google.com>
Because ARM hardware is not yet capable of direct PPI injection into
guests, guest counters trigger physical PMU interrupts that must be
handled by the host PMU interrupt handler.
In armv8pmu_handle_irq(), clear the overflow flags in hardware for host
counters, restrict host perf event handling to bits in cpuc->cntr_mask,
and pass the overflow flags to kvm_pmu_handle_guest_irq(). The KVM hook
clears the guest counter overflow flags in hardware and records them in
the running vCPU's virtual PMOVSSET_EL0 register for subsequent guest
interrupt injection.
Additionally, provide kvm_pmu_host_start() and kvm_pmu_host_stop() so
that armv8pmu_start() and armv8pmu_stop() toggle MDCR_EL2.HPME rather
than PMCR_EL0.E when a partitioned guest's PMU state is loaded on the
CPU.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
arch/arm/include/asm/arm_pmuv3.h | 8 ++++
arch/arm64/include/asm/arm_pmuv3.h | 5 +++
arch/arm64/kvm/pmu-direct.c | 67 ++++++++++++++++++++++++++++++
drivers/perf/arm_pmuv3.c | 34 ++++++++-------
include/kvm/arm_pmu.h | 6 +++
5 files changed, 104 insertions(+), 16 deletions(-)
diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
index cec26c12bc009..9b8d6bbbe70cf 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);
}
+static inline u32 read_pmintenset(void)
+{
+ return read_sysreg(PMINTENSET);
+}
+
static inline void write_pmintenclr(u32 val)
{
write_sysreg(val, PMINTENCLR);
@@ -235,6 +240,9 @@ static inline u64 kvm_pmu_host_counter_mask(void)
{
return ~0;
}
+static inline void kvm_pmu_handle_guest_irq(u64 pmovsr) {}
+static inline bool kvm_pmu_host_start(void) { return false; }
+static inline bool kvm_pmu_host_stop(void) { return false; }
/* 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/arm_pmuv3.h
index cf2b2212e00a2..9037a28365951 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);
}
+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 1ada09d54f68f..95b5a64052762 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -512,3 +512,70 @@ void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu)
kvm_pmu_load(vcpu);
}
}
+
+/**
+ * kvm_pmu_handle_guest_irq() - Record IRQs in guest counters
+ * @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(u64 pmovsr)
+{
+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
+ u64 mask = kvm_pmu_guest_counter_mask();
+ u64 govf = pmovsr & mask;
+ int i;
+
+ write_pmovsclr(govf);
+
+ if (!vcpu || !vcpu->arch.pmu.loaded_on_cpu)
+ return;
+
+ for_each_set_bit(i, (unsigned long *)&govf, 64)
+ set_bit(i, (unsigned long *)__ctxt_sys_reg(&vcpu->arch.ctxt, PMOVSSET_EL0));
+}
+
+/**
+ * kvm_pmu_host_start() - Enable host PMU counters while guest owns PMU
+ *
+ * When a partitioned guest owns the PMU, host counters (HPMN..N-1) are
+ * gated by MDCR_EL2.HPME rather than PMCR_EL0.E.
+ *
+ * Return: True if handled via MDCR_EL2.HPME, false if caller should use PMCR_EL0.E
+ */
+bool kvm_pmu_host_start(void)
+{
+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
+
+ if (!vcpu || !kvm_pmu_is_partitioned(vcpu->kvm) ||
+ !vcpu->arch.pmu.loaded_on_cpu)
+ return false;
+
+ vcpu->arch.mdcr_el2 |= MDCR_EL2_HPME;
+ write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
+ isb();
+ return true;
+}
+
+/**
+ * kvm_pmu_host_stop() - Disable host PMU counters while guest owns PMU
+ *
+ * When a partitioned guest owns the PMU, host counters (HPMN..N-1) are
+ * gated by MDCR_EL2.HPME rather than PMCR_EL0.E.
+ *
+ * Return: True if handled via MDCR_EL2.HPME, false if caller should use PMCR_EL0.E
+ */
+bool kvm_pmu_host_stop(void)
+{
+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
+
+ if (!vcpu || !kvm_pmu_is_partitioned(vcpu->kvm) ||
+ !vcpu->arch.pmu.loaded_on_cpu)
+ return false;
+
+ vcpu->arch.mdcr_el2 &= ~MDCR_EL2_HPME;
+ write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
+ isb();
+ return true;
+}
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 4fcdae8021a56..3e5f8207a0fee 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -763,18 +763,9 @@ static void armv8pmu_disable_event_irq(struct perf_event *event)
armv8pmu_disable_intens(BIT(event->hw.idx));
}
-static u64 armv8pmu_getreset_flags(void)
+static u64 armv8pmu_getovf_flags(void)
{
- u64 value;
-
- /* Read */
- value = read_pmovsclr();
-
- /* Write to clear flags */
- value &= ARMV8_PMU_CNT_MASK_ALL;
- write_pmovsclr(value);
-
- return value;
+ return read_pmovsclr() & ARMV8_PMU_CNT_MASK_ALL;
}
static void update_pmuserenr(u64 val)
@@ -864,7 +855,8 @@ static void armv8pmu_start(struct arm_pmu *cpu_pmu)
brbe_enable(cpu_pmu);
/* Enable all counters */
- armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
+ if (!kvm_pmu_host_start())
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
}
static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
@@ -875,7 +867,8 @@ static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
brbe_disable();
/* Disable all counters */
- armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
+ if (!kvm_pmu_host_stop())
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
}
static void read_branch_records(struct pmu_hw_events *cpuc,
@@ -890,16 +883,16 @@ static void read_branch_records(struct pmu_hw_events *cpuc,
static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
{
- u64 pmovsr;
struct perf_sample_data data;
struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs;
+ u64 pmovsr;
int idx;
/*
- * Get and reset the IRQ flags
+ * Get the IRQ flags
*/
- pmovsr = armv8pmu_getreset_flags();
+ pmovsr = armv8pmu_getovf_flags();
/*
* Did an overflow occur?
@@ -907,6 +900,12 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
if (!armv8pmu_has_overflowed(pmovsr))
return IRQ_NONE;
+ /*
+ * Guest flag reset is handled by the kvm hook at the bottom of
+ * this function.
+ */
+ write_pmovsclr(pmovsr & ~kvm_pmu_guest_counter_mask());
+
/*
* Handle the counter(s) overflow(s)
*/
@@ -948,6 +947,9 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
*/
perf_event_overflow(event, &data, regs);
}
+
+ kvm_pmu_handle_guest_irq(pmovsr);
+
armv8pmu_start(cpu_pmu);
return IRQ_HANDLED;
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 0f2e39367d637..955f142a41b94 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -109,6 +109,9 @@ u64 kvm_pmu_guest_counter_mask(void);
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(u64 pmovsr);
+bool kvm_pmu_host_start(void);
+bool kvm_pmu_host_stop(void);
void kvm_pmu_apply_single_event_filter(struct kvm_vcpu *vcpu, u8 idx);
#define kvm_pmu_get_access(vcpu) ((vcpu)->arch.pmu.access)
@@ -274,6 +277,9 @@ static inline u64 kvm_pmu_guest_counter_mask(void)
return 0;
}
+static inline void kvm_pmu_handle_guest_irq(u64 pmovsr) {}
+static inline bool kvm_pmu_host_start(void) { return false; }
+static inline bool kvm_pmu_host_stop(void) { return false; }
static inline void kvm_pmu_apply_single_event_filter(struct kvm_vcpu *vcpu, u8 idx) {}
static inline bool has_kvm_pmu_partition_support(void)
--
2.56.0.rc1.310.g51773c2048-goog
next prev parent reply other threads:[~2026-09-24 17:29 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-24 17:29 [PATCH v9 00/22] ARM64 PMU Partitioning Colton Lewis
2026-09-24 17:29 ` [PATCH v9 01/22] arm64: cpufeature: Add cpucap for HPMN0 Colton Lewis
2026-09-24 17:29 ` [PATCH v9 02/22] KVM: arm64: Reorganize PMU includes Colton Lewis
2026-09-24 17:29 ` [PATCH v9 03/22] KVM: arm64: Reorganize PMU functions Colton Lewis
2026-09-24 17:29 ` [PATCH v9 04/22] perf: arm_pmuv3: Generalize counter bitmasks Colton Lewis
2026-09-24 17:29 ` [PATCH v9 05/22] perf: arm_pmuv3: Move counter allocation mask to per-CPU struct pmu_hw_events Colton Lewis
2026-09-24 17:29 ` [PATCH v9 06/22] perf: arm_pmuv3: Check cntr_mask before using pmccntr Colton Lewis
2026-09-24 17:29 ` [PATCH v9 07/22] perf: arm_pmuv3: Allocate counter indices from high to low Colton Lewis
2026-09-24 17:29 ` [PATCH v9 08/22] KVM: arm64: Add initial scaffolding for Partitioned PMU Colton Lewis
2026-09-24 17:29 ` [PATCH v9 09/22] KVM: arm64: Set up FGT " Colton Lewis
2026-09-24 17:29 ` [PATCH v9 10/22] KVM: arm64: Add Partitioned PMU register trap handlers Colton Lewis
2026-09-24 17:29 ` [PATCH v9 11/22] KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU Colton Lewis
2026-09-24 17:29 ` [PATCH v9 12/22] KVM: arm64: Context swap Partitioned PMU guest registers Colton Lewis
2026-09-24 17:29 ` [PATCH v9 13/22] KVM: arm64: Enforce PMU event filter at vcpu_load() Colton Lewis
2026-09-24 17:29 ` [PATCH v9 14/22] perf: Add perf_pmu_resched_update() Colton Lewis
2026-09-24 17:29 ` [PATCH v9 15/22] KVM: arm64: Allow kvm_vcpu_pmu_resync_el0() to resync filters in process context Colton Lewis
2026-09-24 17:29 ` [PATCH v9 16/22] KVM: arm64: Apply dynamic guest counter reservations Colton Lewis
2026-09-30 15:28 ` James Clark
2026-10-01 21:33 ` Colton Lewis
2026-09-24 17:29 ` [PATCH v9 17/22] KVM: arm64: Implement lazy PMU context swaps Colton Lewis
2026-09-24 17:29 ` Colton Lewis [this message]
2026-09-24 17:29 ` [PATCH v9 19/22] KVM: arm64: Detect overflows for the Partitioned PMU Colton Lewis
2026-09-24 17:29 ` [PATCH v9 20/22] KVM: arm64: Add vCPU device attr to partition the PMU Colton Lewis
2026-09-30 15:27 ` James Clark
2026-10-01 21:21 ` Colton Lewis
2026-09-24 17:29 ` [PATCH v9 21/22] KVM: selftests: Add find_bit to KVM library Colton Lewis
2026-09-24 17:29 ` [PATCH v9 22/22] KVM: arm64: selftests: Add test case for Partitioned PMU Colton Lewis
2026-09-30 15:25 ` [PATCH v9 00/22] ARM64 PMU Partitioning James Clark
2026-10-01 21:33 ` Colton Lewis
2026-09-30 15:26 ` James Clark
2026-10-01 21:33 ` Colton Lewis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260924172928.2110956-19-coltonlewis@google.com \
--to=coltonlewis@google.com \
--cc=acme@kernel.org \
--cc=alexandru.elisei@arm.com \
--cc=catalin.marinas@arm.com \
--cc=corbet@lwn.net \
--cc=fuad.tabba@linux.dev \
--cc=gankulkarni@os.amperecomputing.com \
--cc=james.clark@linaro.org \
--cc=joey.gouly@arm.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=mingo@redhat.com \
--cc=mizhang@google.com \
--cc=namhyung@kernel.org \
--cc=oliver.upton@linux.dev \
--cc=oupton@kernel.org \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=robin.murphy@arm.com \
--cc=shuah@kernel.org \
--cc=suzuki.poulose@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
--cc=zide.chen@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox