* [PATCH v7 10/20] KVM: arm64: Context swap Partitioned PMU guest registers
From: Colton Lewis @ 2026-05-04 21:18 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
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 <coltonlewis@google.com>
---
arch/arm/include/asm/arm_pmuv3.h | 4 +
arch/arm64/kvm/arm.c | 2 +
arch/arm64/kvm/pmu-direct.c | 169 +++++++++++++++++++++++++++++++
include/kvm/arm_pmu.h | 16 +++
4 files changed, 191 insertions(+)
diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
index 42d62aa48d0a6..eebc89bdab7a1 100644
--- a/arch/arm/include/asm/arm_pmuv3.h
+++ b/arch/arm/include/asm/arm_pmuv3.h
@@ -235,6 +235,10 @@ static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
{
return false;
}
+static inline u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
+{
+ return ~0;
+}
/* PMU Version in DFR Register */
#define ARMV8_PMU_DFR_VER_NI 0
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 410ffd41fd73a..a942f2bc13fc4 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -680,6 +680,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
kvm_vcpu_load_vhe(vcpu);
kvm_arch_vcpu_load_fp(vcpu);
kvm_vcpu_pmu_restore_guest(vcpu);
+ kvm_pmu_load(vcpu);
if (kvm_arm_is_pvtime_enabled(&vcpu->arch))
kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
@@ -721,6 +722,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
kvm_timer_vcpu_put(vcpu);
kvm_vgic_put(vcpu);
kvm_vcpu_pmu_restore_host(vcpu);
+ kvm_pmu_put(vcpu);
if (vcpu_has_nv(vcpu))
kvm_vcpu_put_hw_mmu(vcpu);
kvm_arm_vmid_clear_active();
diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 63ac72910e4b5..360d022d918d5 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -9,6 +9,7 @@
#include <linux/perf/arm_pmuv3.h>
#include <asm/arm_pmuv3.h>
+#include <asm/kvm_emulate.h>
/**
* has_host_pmu_partition_support() - Determine if partitioning is possible
@@ -98,3 +99,171 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
return *host_data_ptr(nr_event_counters);
}
+
+/**
+ * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved counters
+ * @pmu: Pointer to arm_pmu struct
+ *
+ * Compute the bitmask that selects the host-reserved counters in the
+ * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
+ * in HPMN..N
+ *
+ * Return: Bitmask
+ */
+u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
+{
+ u8 nr_counters = *host_data_ptr(nr_event_counters);
+
+ if (kvm_pmu_is_partitioned(pmu))
+ return GENMASK(nr_counters - 1, pmu->max_guest_counters);
+
+ return ARMV8_PMU_CNT_MASK_ALL;
+}
+
+/**
+ * kvm_pmu_guest_counter_mask() - Compute bitmask of guest-reserved counters
+ * @pmu: Pointer to arm_pmu struct
+ *
+ * Compute the bitmask that selects the guest-reserved counters in the
+ * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
+ * in 0..HPMN and the cycle and instruction counters.
+ *
+ * Return: Bitmask
+ */
+u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu)
+{
+ if (kvm_pmu_is_partitioned(pmu))
+ return ARMV8_PMU_CNT_MASK_C | GENMASK(pmu->max_guest_counters - 1, 0);
+
+ return 0;
+}
+
+/**
+ * kvm_pmu_load() - Load untrapped PMU registers
+ * @vcpu: Pointer to struct kvm_vcpu
+ *
+ * Load all untrapped PMU registers from the VCPU into the PCPU. Mask
+ * to only bits belonging to guest-reserved counters and leave
+ * host-reserved counters alone in bitmask registers.
+ */
+void kvm_pmu_load(struct kvm_vcpu *vcpu)
+{
+ struct arm_pmu *pmu;
+ unsigned long guest_counters;
+ u64 mask;
+ u8 i;
+ u64 val;
+
+ /*
+ * If we aren't guest-owned then we know the guest isn't using
+ * the PMU anyway, so no need to bother with the swap.
+ */
+ if (!kvm_vcpu_pmu_is_partitioned(vcpu))
+ return;
+
+ preempt_disable();
+
+ pmu = vcpu->kvm->arch.arm_pmu;
+ guest_counters = kvm_pmu_guest_counter_mask(pmu);
+
+ for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) {
+ val = __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i);
+
+ if (i == ARMV8_PMU_CYCLE_IDX) {
+ write_sysreg(val, pmccntr_el0);
+ } else {
+ write_sysreg(i, pmselr_el0);
+ write_sysreg(val, pmxevcntr_el0);
+ }
+ }
+
+ val = __vcpu_sys_reg(vcpu, PMSELR_EL0);
+ write_sysreg(val, pmselr_el0);
+
+ /* Save only the stateful writable bits. */
+ val = __vcpu_sys_reg(vcpu, PMCR_EL0);
+ mask = 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 = 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 = __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
+ write_sysreg(val & mask, pmcntenset_el0);
+ write_sysreg(~val & mask, pmcntenclr_el0);
+
+ val = __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
+ write_sysreg(val & mask, pmintenset_el1);
+ write_sysreg(~val & mask, pmintenclr_el1);
+
+ preempt_enable();
+}
+
+/**
+ * kvm_pmu_put() - Put untrapped PMU registers
+ * @vcpu: Pointer to struct kvm_vcpu
+ *
+ * Put all untrapped PMU registers from the VCPU into the PCPU. Mask
+ * to only bits belonging to guest-reserved counters and leave
+ * host-reserved counters alone in bitmask registers.
+ */
+void kvm_pmu_put(struct kvm_vcpu *vcpu)
+{
+ struct arm_pmu *pmu;
+ unsigned long guest_counters;
+ u64 mask;
+ u8 i;
+ u64 val;
+
+ /*
+ * If we aren't guest-owned then we know the guest is not
+ * accessing the PMU anyway, so no need to bother with the
+ * swap.
+ */
+ if (!kvm_vcpu_pmu_is_partitioned(vcpu))
+ return;
+
+ preempt_disable();
+
+ pmu = vcpu->kvm->arch.arm_pmu;
+ guest_counters = kvm_pmu_guest_counter_mask(pmu);
+
+ for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) {
+ if (i == ARMV8_PMU_CYCLE_IDX) {
+ val = read_sysreg(pmccntr_el0);
+ } else {
+ write_sysreg(i, pmselr_el0);
+ val = read_sysreg(pmxevcntr_el0);
+ }
+
+ __vcpu_assign_sys_reg(vcpu, PMEVCNTR0_EL0 + i, val);
+ }
+
+ val = read_sysreg(pmselr_el0);
+ __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, val);
+
+ val = read_sysreg(pmcr_el0);
+ __vcpu_assign_sys_reg(vcpu, PMCR_EL0, val);
+
+ /* Mask these to only save the guest relevant bits. */
+ mask = kvm_pmu_guest_counter_mask(pmu);
+
+ val = read_sysreg(pmcntenset_el0);
+ __vcpu_assign_sys_reg(vcpu, PMCNTENSET_EL0, val & mask);
+
+ val = read_sysreg(pmintenset_el1);
+ __vcpu_assign_sys_reg(vcpu, PMINTENSET_EL1, val & mask);
+
+ preempt_enable();
+}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 6aaeb27642540..fa881dc5f5832 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -96,6 +96,10 @@ bool kvm_pmu_is_partitioned(struct arm_pmu *pmu);
u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu);
u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu);
+u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu);
+u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu);
+void kvm_pmu_load(struct kvm_vcpu *vcpu);
+void kvm_pmu_put(struct kvm_vcpu *vcpu);
#if !defined(__KVM_NVHE_HYPERVISOR__)
bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu);
@@ -167,6 +171,8 @@ static inline u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
{
return 0;
}
+static inline void kvm_pmu_load(struct kvm_vcpu *vcpu) {}
+static inline void kvm_pmu_put(struct kvm_vcpu *vcpu) {}
static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
u64 select_idx, u64 val) {}
static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu,
@@ -269,6 +275,16 @@ static inline bool kvm_pmu_is_partitioned(void *pmu)
return false;
}
+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
#endif
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 09/20] KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU
From: Colton Lewis @ 2026-05-04 21:18 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
Set up MDCR_EL2 to handle a Partitioned PMU. That means calculate an
appropriate value for HPMN instead of the default maximum setting the
host allows (which implies no partition) so hardware enforces that a
guest will only see the counters in the guest partition.
Setting HPMN to a non default value means the global enable bit for
the host counters is now MDCR_EL2.HPME instead of the usual
PMCR_EL0.E. Enable the HPME bit to allow the host to count guest
events. Since HPME only has an effect when HPMN is set which we only
do for the guest, it is correct to enable it unconditionally here.
Unset the TPM and TPMCR bits, which trap all PMU accesses, if
FGT (fine grain trapping) is being used.
If available, set the filtering bits HPMD and HCCD to be extra sure
nothing in the guest counts at EL2.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
arch/arm64/kvm/debug.c | 29 ++++++++++++++++++++++++++---
arch/arm64/kvm/pmu-direct.c | 24 ++++++++++++++++++++++++
arch/arm64/kvm/pmu.c | 7 +++++++
include/kvm/arm_pmu.h | 11 +++++++++++
4 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index 3ad6b7c6e4ba7..0ab89c91e19cb 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -36,20 +36,43 @@ static int cpu_has_spe(u64 dfr0)
*/
static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
{
+ int hpmn = kvm_pmu_hpmn(vcpu);
+
preempt_disable();
/*
* 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 = FIELD_PREP(MDCR_EL2_HPMN,
- *host_data_ptr(nr_event_counters));
+
+ vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, hpmn);
vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
MDCR_EL2_TPMS |
MDCR_EL2_TTRF |
MDCR_EL2_TPMCR |
MDCR_EL2_TDRA |
- MDCR_EL2_TDOSA);
+ MDCR_EL2_TDOSA |
+ MDCR_EL2_HPME);
+
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ /*
+ * Filtering these should be redundant because we trap
+ * all the TYPER and FILTR registers anyway and ensure
+ * they filter EL2, but set the bits if they are here.
+ */
+ if (is_pmuv3p1(read_pmuver()))
+ vcpu->arch.mdcr_el2 |= MDCR_EL2_HPMD;
+ if (is_pmuv3p5(read_pmuver()))
+ vcpu->arch.mdcr_el2 |= MDCR_EL2_HCCD;
+
+ /*
+ * Take out the coarse grain traps if we are using
+ * fine grain traps.
+ */
+ if (kvm_vcpu_pmu_use_fgt(vcpu))
+ vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR);
+
+ }
/* Is the VM being debugged by userspace? */
if (vcpu->guest_debug)
diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 2148bc46079c4..63ac72910e4b5 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -74,3 +74,27 @@ bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu)
cpus_have_final_cap(ARM64_HAS_FGT) &&
(hpmn != 0 || cpus_have_final_cap(ARM64_HAS_HPMN0));
}
+
+/**
+ * kvm_pmu_hpmn() - Calculate HPMN field value
+ * @vcpu: Pointer to struct kvm_vcpu
+ *
+ * Calculate the appropriate value to set for MDCR_EL2.HPMN. If
+ * partitioned, this is the number of counters set for the guest if
+ * supported, falling back to max_guest_counters if needed. If we are not
+ * partitioned or can't set the implied HPMN value, fall back to the
+ * host value.
+ *
+ * Return: A valid HPMN value
+ */
+u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
+{
+ u8 nr_guest_cntr = vcpu->kvm->arch.nr_pmu_counters;
+
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)
+ && !vcpu_on_unsupported_cpu(vcpu)
+ && (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0))
+ return nr_guest_cntr;
+
+ return *host_data_ptr(nr_event_counters);
+}
diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c
index ee2f0f7e61bcf..8c10ad05661bc 100644
--- a/arch/arm64/kvm/pmu.c
+++ b/arch/arm64/kvm/pmu.c
@@ -542,6 +542,13 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS))
return 1;
+ /*
+ * If partitioned then we are limited by the max counters in
+ * the guest partition.
+ */
+ if (kvm_pmu_is_partitioned(arm_pmu))
+ return arm_pmu->max_guest_counters;
+
/*
* The arm_pmu->cntr_mask considers the fixed counter(s) as well.
* Ignore those and return only the general-purpose counters.
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index c3987e0c01775..6aaeb27642540 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -94,6 +94,9 @@ void kvm_vcpu_pmu_resync_el0(void);
bool kvm_pmu_is_partitioned(struct arm_pmu *pmu);
+u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu);
+u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu);
+
#if !defined(__KVM_NVHE_HYPERVISOR__)
bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu);
bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu);
@@ -156,6 +159,14 @@ static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu)
{
return false;
}
+static inline u8 kvm_pmu_guest_num_counters(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
+static inline u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
u64 select_idx, u64 val) {}
static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu,
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 08/20] KVM: arm64: Add Partitioned PMU register trap handlers
From: Colton Lewis @ 2026-05-04 21:18 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
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 <coltonlewis@google.com>
---
arch/arm64/kvm/sys_regs.c | 236 +++++++++++++++++++++++++++++++-------
1 file changed, 197 insertions(+), 39 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 0a8e8ee69cd00..cc3d1804ab200 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -985,9 +985,25 @@ static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
return __vcpu_sys_reg(vcpu, r->reg);
}
+static void pmu_write_pmuserenr(struct kvm_vcpu *vcpu, u64 val)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu))
+ write_sysreg(val, pmuserenr_el0);
+ else
+ __vcpu_assign_sys_reg(vcpu, PMUSERENR_EL0, val);
+}
+
+static u64 pmu_read_pmuserenr(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu))
+ return read_sysreg(pmuserenr_el0);
+ else
+ return __vcpu_sys_reg(vcpu, PMUSERENR_EL0);
+}
+
static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags)
{
- u64 reg = __vcpu_sys_reg(vcpu, PMUSERENR_EL0);
+ u64 reg = pmu_read_pmuserenr(vcpu);
bool enabled = (reg & flags) || vcpu_mode_priv(vcpu);
if (!enabled)
@@ -1016,6 +1032,29 @@ static bool pmu_access_event_counter_el0_disabled(struct kvm_vcpu *vcpu)
return check_pmu_access_disabled(vcpu, ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_EN);
}
+static void pmu_write_pmcr(struct kvm_vcpu *vcpu, u64 val)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ write_sysreg(val, pmcr_el0);
+ return;
+ }
+
+ kvm_pmu_handle_pmcr(vcpu, val);
+}
+
+static u64 pmu_read_pmcr(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ return u64_replace_bits(
+ read_sysreg(pmcr_el0),
+ vcpu->kvm->arch.nr_pmu_counters,
+ ARMV8_PMU_PMCR_N);
+ }
+
+ return kvm_vcpu_read_pmcr(vcpu);
+
+}
+
static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -1026,18 +1065,17 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
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 = kvm_vcpu_read_pmcr(vcpu);
+ val = pmu_read_pmcr(vcpu);
val &= ~ARMV8_PMU_PMCR_MASK;
val |= p->regval & ARMV8_PMU_PMCR_MASK;
if (!kvm_supports_32bit_el0())
val |= ARMV8_PMU_PMCR_LC;
- kvm_pmu_handle_pmcr(vcpu, val);
+ pmu_write_pmcr(vcpu, val);
} else {
/* PMCR.P & PMCR.C are RAZ */
- val = kvm_vcpu_read_pmcr(vcpu)
+ val = pmu_read_pmcr(vcpu)
& ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C);
p->regval = val;
}
@@ -1045,6 +1083,24 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
return true;
}
+static void pmu_write_pmselr(struct kvm_vcpu *vcpu, u64 val)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ write_sysreg(val, pmselr_el0);
+ return;
+ }
+
+ __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, val);
+}
+
+static u64 pmu_read_pmselr(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu))
+ return read_sysreg(pmselr_el0);
+
+ return __vcpu_sys_reg(vcpu, PMSELR_EL0);
+}
+
static bool access_pmselr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -1052,10 +1108,10 @@ static bool access_pmselr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
return false;
if (p->is_write)
- __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, p->regval);
+ pmu_write_pmselr(vcpu, p->regval);
else
/* return PMSELR.SEL field */
- p->regval = __vcpu_sys_reg(vcpu, PMSELR_EL0)
+ p->regval = pmu_read_pmselr(vcpu)
& PMSELR_EL0_SEL_MASK;
return true;
@@ -1128,6 +1184,44 @@ static int set_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
return 0;
}
+static void pmu_write_evcntr(struct kvm_vcpu *vcpu, u64 val, u64 idx)
+{
+ u64 pmselr;
+
+ if (!kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ kvm_pmu_set_counter_value(vcpu, idx, val);
+ return;
+ }
+
+ if (idx == ARMV8_PMU_CYCLE_IDX) {
+ write_sysreg(val, pmccntr_el0);
+ return;
+ }
+
+ pmselr = read_sysreg(pmselr_el0);
+ write_sysreg(idx, pmselr_el0);
+ write_sysreg(val, pmxevcntr_el0);
+ write_sysreg(pmselr, pmselr_el0);
+}
+
+static u64 pmu_read_evcntr(struct kvm_vcpu *vcpu, u64 idx)
+{
+ u64 pmselr;
+ u64 val;
+
+ if (!kvm_vcpu_pmu_is_partitioned(vcpu))
+ return kvm_pmu_get_counter_value(vcpu, idx);
+
+ if (idx == ARMV8_PMU_CYCLE_IDX)
+ return read_sysreg(pmccntr_el0);
+
+ pmselr = read_sysreg(pmselr_el0);
+ write_sysreg(idx, pmselr_el0);
+ val = read_sysreg(pmxevcntr_el0);
+ write_sysreg(pmselr, pmselr_el0);
+ return val;
+}
+
static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
@@ -1141,7 +1235,7 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
return false;
idx = SYS_FIELD_GET(PMSELR_EL0, SEL,
- __vcpu_sys_reg(vcpu, PMSELR_EL0));
+ pmu_read_pmselr(vcpu));
} else if (r->Op2 == 0) {
/* PMCCNTR_EL0 */
if (pmu_access_cycle_counter_el0_disabled(vcpu))
@@ -1173,14 +1267,34 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
if (pmu_access_el0_disabled(vcpu))
return false;
- kvm_pmu_set_counter_value(vcpu, idx, p->regval);
+ pmu_write_evcntr(vcpu, p->regval, idx);
} else {
- p->regval = kvm_pmu_get_counter_value(vcpu, idx);
+ p->regval = pmu_read_evcntr(vcpu, idx);
}
return true;
}
+
+static void pmu_write_evtyper(struct kvm_vcpu *vcpu, u64 val, u64 idx)
+{
+ u64 mask;
+
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ mask = kvm_pmu_evtyper_mask(vcpu->kvm);
+ __vcpu_assign_sys_reg(vcpu, PMEVTYPER0_EL0 + idx, val & mask);
+ return;
+ }
+
+ kvm_pmu_set_counter_event_type(vcpu, val, idx);
+ kvm_vcpu_pmu_restore_guest(vcpu);
+}
+
+static u64 pmu_read_evtyper(struct kvm_vcpu *vcpu, u64 idx)
+{
+ return __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + idx);
+}
+
static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -1191,7 +1305,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 1) {
/* PMXEVTYPER_EL0 */
- idx = SYS_FIELD_GET(PMSELR_EL0, SEL, __vcpu_sys_reg(vcpu, PMSELR_EL0));
+ idx = SYS_FIELD_GET(PMSELR_EL0, SEL, pmu_read_pmselr(vcpu));
reg = PMEVTYPER0_EL0 + idx;
} else if (r->CRn == 14 && (r->CRm & 12) == 12) {
idx = ((r->CRm & 3) << 3) | (r->Op2 & 7);
@@ -1207,12 +1321,10 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (!pmu_counter_idx_valid(vcpu, idx))
return false;
- if (p->is_write) {
- kvm_pmu_set_counter_event_type(vcpu, p->regval, idx);
- kvm_vcpu_pmu_restore_guest(vcpu);
- } else {
- p->regval = __vcpu_sys_reg(vcpu, reg);
- }
+ if (p->is_write)
+ pmu_write_evtyper(vcpu, p->regval, idx);
+ else
+ p->regval = pmu_read_evtyper(vcpu, idx);
return true;
}
@@ -1235,6 +1347,35 @@ static int get_pmreg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, u64 *v
return 0;
}
+static void pmu_write_pmcnten(struct kvm_vcpu *vcpu, u64 val, bool set)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ if (set)
+ write_sysreg(val, pmcntenset_el0);
+ else
+ write_sysreg(val, pmcntenclr_el0);
+
+ return;
+ }
+
+ if (set)
+ /* accessing PMCNTENSET_EL0 */
+ __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, |=, val);
+ else
+ /* accessing PMCNTENCLR_EL0 */
+ __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, &=, ~val);
+
+ kvm_pmu_reprogram_counter_mask(vcpu, val);
+}
+
+static u64 pmu_read_pmcnten(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu))
+ return read_sysreg(pmcntenset_el0);
+
+ return __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
+}
+
static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
@@ -1246,40 +1387,58 @@ static bool access_pmcnten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
mask = kvm_pmu_accessible_counter_mask(vcpu);
if (p->is_write) {
val = p->regval & mask;
- if (r->Op2 & 0x1)
- /* accessing PMCNTENSET_EL0 */
- __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, |=, val);
- else
- /* accessing PMCNTENCLR_EL0 */
- __vcpu_rmw_sys_reg(vcpu, PMCNTENSET_EL0, &=, ~val);
-
- kvm_pmu_reprogram_counter_mask(vcpu, val);
+ pmu_write_pmcnten(vcpu, val, r->Op2 & 0x1);
} else {
- p->regval = __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
+ p->regval = pmu_read_pmcnten(vcpu);
}
return true;
}
+static void pmu_write_pminten(struct kvm_vcpu *vcpu, u64 val, bool set)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu)) {
+ if (set)
+ write_sysreg(val, pmintenset_el1);
+ else
+ write_sysreg(val, pmintenclr_el1);
+
+ return;
+ }
+
+ if (set)
+ /* accessing PMINTENSET_EL1 */
+ __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, |=, val);
+ else
+ /* accessing PMINTENCLR_EL1 */
+ __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=, ~val);
+
+ kvm_pmu_reprogram_counter_mask(vcpu, val);
+}
+
+static u64 pmu_read_pminten(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vcpu_pmu_is_partitioned(vcpu))
+ return read_sysreg(pmintenset_el1);
+
+ return __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
+}
+
static bool access_pminten(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 mask = kvm_pmu_accessible_counter_mask(vcpu);
+ u64 val, mask;
if (check_pmu_access_disabled(vcpu, 0))
return false;
+ mask = kvm_pmu_accessible_counter_mask(vcpu);
if (p->is_write) {
- u64 val = p->regval & mask;
+ val = p->regval & mask;
- if (r->Op2 & 0x1)
- /* accessing PMINTENSET_EL1 */
- __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, |=, val);
- else
- /* accessing PMINTENCLR_EL1 */
- __vcpu_rmw_sys_reg(vcpu, PMINTENSET_EL1, &=, ~val);
+ pmu_write_pminten(vcpu, val, r->Op2 & 0x1);
} else {
- p->regval = __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
+ p->regval = pmu_read_pminten(vcpu);
}
return true;
@@ -1330,10 +1489,9 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (!vcpu_mode_priv(vcpu))
return undef_access(vcpu, p, r);
- __vcpu_assign_sys_reg(vcpu, PMUSERENR_EL0,
- (p->regval & ARMV8_PMU_USERENR_MASK));
+ pmu_write_pmuserenr(vcpu, p->regval & ARMV8_PMU_USERENR_MASK);
} else {
- p->regval = __vcpu_sys_reg(vcpu, PMUSERENR_EL0)
+ p->regval = pmu_read_pmuserenr(vcpu)
& ARMV8_PMU_USERENR_MASK;
}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 07/20] KVM: arm64: Set up FGT for Partitioned PMU
From: Colton Lewis @ 2026-05-04 21:18 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
In order to gain the best performance benefit from partitioning the
PMU, utilize fine grain traps (FEAT_FGT and FEAT_FGT2) to avoid
trapping common PMU register accesses by the guest to remove that
overhead.
Untrapped:
* PMCR_EL0
* PMUSERENR_EL0
* PMSELR_EL0
* PMCCNTR_EL0
* PMCNTEN_EL0
* PMINTEN_EL1
* PMEVCNTRn_EL0
These are safe to untrap because writing MDCR_EL2.HPMN as this series
will do limits the effect of writes to any of these registers to the
partition of counters 0..HPMN-1. Reads from these registers will not
leak information from between guests as all these registers are
context swapped by a later patch in this series. Reads from these
registers also do not leak any information about the host's hardware
beyond what is promised by PMUv3.
Trapped:
* PMOVS_EL0
* PMEVTYPERn_EL0
* PMCCFILTR_EL0
* PMICNTR_EL0
* PMICFILTR_EL0
* PMCEIDn_EL0
* PMMIR_EL1
PMOVS remains trapped so KVM can track overflow IRQs that will need to
be injected into the guest.
PMICNTR and PMIFILTR remain trapped because KVM is not handling them
yet.
PMEVTYPERn remains trapped so KVM can limit which events guests can
count, such as disallowing counting at EL2. PMCCFILTR and PMCIFILTR
are special cases of the same.
PMCEIDn and PMMIR remain trapped because they can leak information
specific to the host hardware implementation.
NOTE: This patch temporarily forces kvm_vcpu_pmu_is_partitioned() to
be false to prevent partial feature activation for easier debugging.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
arch/arm/include/asm/arm_pmuv3.h | 4 +++
arch/arm64/kvm/config.c | 41 ++++++++++++++++++++++--
arch/arm64/kvm/pmu-direct.c | 54 ++++++++++++++++++++++++++++++++
include/kvm/arm_pmu.h | 30 ++++++++++++++++++
4 files changed, 126 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
index 154503f054886..42d62aa48d0a6 100644
--- a/arch/arm/include/asm/arm_pmuv3.h
+++ b/arch/arm/include/asm/arm_pmuv3.h
@@ -231,6 +231,10 @@ static inline bool kvm_set_pmuserenr(u64 val)
}
static inline void kvm_vcpu_pmu_resync_el0(void) {}
+static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
+{
+ return false;
+}
/* PMU Version in DFR Register */
#define ARMV8_PMU_DFR_VER_NI 0
diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c
index d9f553cbf9dfd..3b5f028f5bf11 100644
--- a/arch/arm64/kvm/config.c
+++ b/arch/arm64/kvm/config.c
@@ -1598,12 +1598,47 @@ static void __compute_hfgwtr(struct kvm_vcpu *vcpu)
*vcpu_fgt(vcpu, HFGWTR_EL2) |= HFGWTR_EL2_TCR_EL1;
}
+static void __compute_hdfgrtr(struct kvm_vcpu *vcpu)
+{
+ __compute_fgt(vcpu, HDFGRTR_EL2);
+
+ *vcpu_fgt(vcpu, HDFGRTR_EL2) |=
+ 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);
if (is_hyp_ctxt(vcpu))
*vcpu_fgt(vcpu, HDFGWTR_EL2) |= HDFGWTR_EL2_MDSCR_EL1;
+
+ *vcpu_fgt(vcpu, HDFGWTR_EL2) |=
+ 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) &=
+ ~(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) &=
+ ~(HDFGWTR2_EL2_nPMICFILTR_EL0
+ | HDFGWTR2_EL2_nPMICNTR_EL0);
}
void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
@@ -1614,7 +1649,7 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
__compute_fgt(vcpu, HFGRTR_EL2);
__compute_hfgwtr(vcpu);
__compute_fgt(vcpu, HFGITR_EL2);
- __compute_fgt(vcpu, HDFGRTR_EL2);
+ __compute_hdfgrtr(vcpu);
__compute_hdfgwtr(vcpu);
__compute_fgt(vcpu, HAFGRTR_EL2);
@@ -1624,6 +1659,6 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
__compute_fgt(vcpu, HFGRTR2_EL2);
__compute_fgt(vcpu, HFGWTR2_EL2);
__compute_fgt(vcpu, HFGITR2_EL2);
- __compute_fgt(vcpu, HDFGRTR2_EL2);
- __compute_fgt(vcpu, HDFGWTR2_EL2);
+ __compute_hdfgrtr2(vcpu);
+ __compute_hdfgwtr2(vcpu);
}
diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 74e40e4915416..2148bc46079c4 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -5,6 +5,8 @@
*/
#include <linux/kvm_host.h>
+#include <linux/perf/arm_pmu.h>
+#include <linux/perf/arm_pmuv3.h>
#include <asm/arm_pmuv3.h>
@@ -20,3 +22,55 @@ bool has_host_pmu_partition_support(void)
return has_vhe() &&
system_supports_pmuv3();
}
+
+/**
+ * kvm_pmu_is_partitioned() - Determine if given PMU is partitioned
+ * @pmu: Pointer to arm_pmu struct
+ *
+ * Determine if given PMU is partitioned by looking at hpmn field. The
+ * PMU is partitioned if this field is less than the number of
+ * counters in the system.
+ *
+ * Return: True if the PMU is partitioned, false otherwise
+ */
+bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
+{
+ if (!pmu)
+ return false;
+
+ return pmu->max_guest_counters >= 0 &&
+ pmu->max_guest_counters <= *host_data_ptr(nr_event_counters);
+}
+
+/**
+ * kvm_vcpu_pmu_is_partitioned() - Determine if given VCPU has a partitioned PMU
+ * @vcpu: Pointer to kvm_vcpu struct
+ *
+ * Determine if given VCPU has a partitioned PMU by extracting that
+ * field and passing it to :c:func:`kvm_pmu_is_partitioned`
+ *
+ * Return: True if the VCPU PMU is partitioned, false otherwise
+ */
+bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu)
+{
+ return kvm_pmu_is_partitioned(vcpu->kvm->arch.arm_pmu) &&
+ false;
+}
+
+/**
+ * kvm_vcpu_pmu_use_fgt() - Determine if we can use FGT
+ * @vcpu: Pointer to struct kvm_vcpu
+ *
+ * Determine if we can use FGT for direct access to registers. We can
+ * if capabilities permit the number of guest counters requested.
+ *
+ * Return: True if we can use FGT, false otherwise
+ */
+bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu)
+{
+ u8 hpmn = vcpu->kvm->arch.nr_pmu_counters;
+
+ return kvm_vcpu_pmu_is_partitioned(vcpu) &&
+ cpus_have_final_cap(ARM64_HAS_FGT) &&
+ (hpmn != 0 || cpus_have_final_cap(ARM64_HAS_HPMN0));
+}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 95f404cdcb2df..c3987e0c01775 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -92,6 +92,23 @@ void kvm_vcpu_pmu_resync_el0(void);
#define kvm_vcpu_has_pmu(vcpu) \
(vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3))
+bool kvm_pmu_is_partitioned(struct arm_pmu *pmu);
+
+#if !defined(__KVM_NVHE_HYPERVISOR__)
+bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu);
+bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu);
+#else
+static inline bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
+
+static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
+#endif
+
/*
* Updates the vcpu's view of the pmu events for this cpu.
* Must be called before every vcpu run after disabling interrupts, to ensure
@@ -131,6 +148,14 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
{
return 0;
}
+static inline bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
+static inline bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
u64 select_idx, u64 val) {}
static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu,
@@ -228,6 +253,11 @@ static inline bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int id
static inline void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) {}
+static inline bool kvm_pmu_is_partitioned(void *pmu)
+{
+ return false;
+}
+
#endif
#endif
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 06/20] perf: arm_pmuv3: Add method to partition the PMU
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
For PMUv3, the register field MDCR_EL2.HPMN partitiones the PMU
counters into two ranges where counters 0..HPMN-1 are accessible by
EL1 and, if allowed, EL0 while counters HPMN..N are only accessible by
EL2.
Create a module parameter reserved_host_counters to reserve a number
of counters for the host. Counters not reserved for the host may be
used by a guest VM when the PMU is partitioned.
Add the function armv8pmu_partition() to check the validity of the
reservation and record a partition has happened and the maximum
allowable value for HPMN.
Due to the difficulty this feature would create for the driver running
in nVHE mode, partitioning is only allowed in VHE mode. In order to
support a partitioning on nVHE we'd need to explicitly disable guest
counters on every exit and reset HPMN to place all counters in the
first range.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
arch/arm/include/asm/arm_pmuv3.h | 4 ++
arch/arm64/include/asm/arm_pmuv3.h | 5 ++
arch/arm64/kvm/Makefile | 2 +-
arch/arm64/kvm/pmu-direct.c | 22 +++++++++
drivers/perf/arm_pmuv3.c | 77 ++++++++++++++++++++++++++++--
include/kvm/arm_pmu.h | 8 ++++
include/linux/perf/arm_pmu.h | 2 +
7 files changed, 115 insertions(+), 5 deletions(-)
create mode 100644 arch/arm64/kvm/pmu-direct.c
diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h
index 2ec0e5e83fc98..154503f054886 100644
--- a/arch/arm/include/asm/arm_pmuv3.h
+++ b/arch/arm/include/asm/arm_pmuv3.h
@@ -221,6 +221,10 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr)
return false;
}
+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/arm_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 == ID_AA64DFR0_EL1_PMUVer_NI);
}
+static inline bool is_pmuv3p1(int pmuver)
+{
+ return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P1;
+}
+
static inline bool is_pmuv3p4(int pmuver)
{
return pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P4;
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 3ebc0570345cc..baf0f296c0e53 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -26,7 +26,7 @@ kvm-y += 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
-kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o
+kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu-direct.o pmu.o
kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o
kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o
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 <coltonlewis@google.com>
+ */
+
+#include <linux/kvm_host.h>
+
+#include <asm/arm_pmuv3.h>
+
+/**
+ * has_host_pmu_partition_support() - Determine if partitioning is possible
+ *
+ * Partitioning is only supported in VHE mode with PMUv3
+ *
+ * Return: True if partitioning is possible, false otherwise
+ */
+bool has_host_pmu_partition_support(void)
+{
+ return has_vhe() &&
+ system_supports_pmuv3();
+}
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 7ff3139dda893..6e447227d801f 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -42,6 +42,13 @@
#define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_ACCESS 0xEC
#define ARMV8_THUNDER_PERFCTR_L1I_CACHE_PREF_MISS 0xED
+static int reserved_host_counters __read_mostly = -1;
+bool armv8pmu_is_partitioned;
+
+module_param(reserved_host_counters, int, 0);
+MODULE_PARM_DESC(reserved_host_counters,
+ "PMU Partition: -1 = No partition; +N = Reserve N counters for the host");
+
/*
* ARMv8 Architectural defined events, not all of these may
* be supported on any given implementation. Unsupported events will
@@ -532,6 +539,11 @@ static void armv8pmu_pmcr_write(u64 val)
write_pmcr(val);
}
+static u64 armv8pmu_pmcr_n_read(void)
+{
+ return FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read());
+}
+
static int armv8pmu_has_overflowed(u64 pmovsr)
{
return !!(pmovsr & ARMV8_PMU_CNT_MASK_ALL);
@@ -1312,6 +1324,54 @@ struct armv8pmu_probe_info {
bool present;
};
+/**
+ * armv8pmu_reservation_is_valid() - Determine if reservation is allowed
+ * @host_counters: Number of host counters to reserve
+ *
+ * Determine if the number of host counters in the argument is an
+ * allowed reservation, 0 to NR_COUNTERS inclusive.
+ *
+ * Return: True if reservation allowed, false otherwise
+ */
+static bool armv8pmu_reservation_is_valid(int host_counters)
+{
+ return host_counters >= 0 &&
+ host_counters <= armv8pmu_pmcr_n_read();
+}
+
+/**
+ * armv8pmu_partition() - Partition the PMU
+ * @pmu: Pointer to pmu being partitioned
+ * @host_counters: Number of host counters to reserve
+ *
+ * Partition the given PMU by taking a number of host counters to
+ * reserve and, if it is a valid reservation, recording the
+ * corresponding HPMN value in the 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", host_counters);
+ return -EINVAL;
+ }
+
+ nr_counters = armv8pmu_pmcr_n_read();
+ hpmn = nr_counters - host_counters;
+
+ pmu->max_guest_counters = hpmn;
+ armv8pmu_is_partitioned = true;
+
+ pr_info("Partitioned PMU with %d host counters -> %u guest counters", host_counters, hpmn);
+
+ return 0;
+}
+
static void __armv8pmu_probe_pmu(void *info)
{
struct armv8pmu_probe_info *probe = info;
@@ -1326,17 +1386,26 @@ static void __armv8pmu_probe_pmu(void *info)
cpu_pmu->pmuver = pmuver;
probe->present = true;
+ cpu_pmu->max_guest_counters = -1;
/* Read the nb of CNTx counters supported from PMNC */
- bitmap_set(cpu_pmu->cntr_mask,
- 0, FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read()));
+ bitmap_set(cpu_pmu->hw_cntr_mask, 0, armv8pmu_pmcr_n_read());
/* Add the CPU cycles counter */
- set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask);
+ set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->hw_cntr_mask);
/* Add the CPU instructions counter */
if (pmuv3_has_icntr())
- set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask);
+ set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->hw_cntr_mask);
+
+ bitmap_copy(cpu_pmu->cntr_mask, cpu_pmu->hw_cntr_mask, ARMPMU_MAX_HWEVENTS);
+
+ if (reserved_host_counters >= 0) {
+ if (has_host_pmu_partition_support())
+ armv8pmu_partition(cpu_pmu, reserved_host_counters);
+ else
+ pr_err("PMU partition is not supported");
+ }
pmceid[0] = pmceid_raw[0] = read_pmceid0();
pmceid[1] = pmceid_raw[1] = read_pmceid1();
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 24a471cf59d56..95f404cdcb2df 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -47,7 +47,10 @@ struct arm_pmu_entry {
struct arm_pmu *arm_pmu;
};
+extern bool armv8pmu_is_partitioned;
+
bool kvm_supports_guest_pmuv3(void);
+bool has_host_pmu_partition_support(void);
#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
@@ -117,6 +120,11 @@ static inline bool kvm_supports_guest_pmuv3(void)
return false;
}
+static inline bool has_host_pmu_partition_support(void)
+{
+ return false;
+}
+
#define kvm_arm_pmu_irq_initialized(v) (false)
static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
u64 select_idx)
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 52b37f7bdbf9e..f7b000bb3eca8 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -109,6 +109,7 @@ struct arm_pmu {
*/
int (*map_pmuv3_event)(unsigned int eventsel);
DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS);
+ DECLARE_BITMAP(hw_cntr_mask, ARMPMU_MAX_HWEVENTS);
bool secure_access; /* 32-bit ARM only */
struct platform_device *plat_device;
struct pmu_hw_events __percpu *hw_events;
@@ -129,6 +130,7 @@ struct arm_pmu {
/* Only to be used by ACPI probing code */
unsigned long acpi_cpuid;
+ int max_guest_counters;
};
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 05/20] perf: arm_pmuv3: Check cntr_mask before using pmccntr
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
Check cntr_mask before using pmccntr to ensure it's available. With a
partitioned PMU, there may be instances where pmccntr is being used by
the guest and will ba absent from cntr_mask.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
drivers/perf/arm_pmuv3.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 1cceb1f614515..7ff3139dda893 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -993,6 +993,9 @@ static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc,
if (evtype != ARMV8_PMUV3_PERFCTR_CPU_CYCLES)
return false;
+ if (!test_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask))
+ return false;
+
/*
* A CPU_CYCLES event with threshold counting cannot use PMCCNTR_EL0
* since it lacks threshold support.
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 04/20] perf: arm_pmuv3: Generalize counter bitmasks
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
The OVSR bitmasks are valid for enable and interrupt registers as well as
overflow registers. Generalize the names.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
drivers/perf/arm_pmuv3.c | 4 ++--
include/linux/perf/arm_pmuv3.h | 14 +++++++-------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 8d3b832cd633a..1cceb1f614515 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -534,7 +534,7 @@ static void armv8pmu_pmcr_write(u64 val)
static int armv8pmu_has_overflowed(u64 pmovsr)
{
- return !!(pmovsr & ARMV8_PMU_OVERFLOWED_MASK);
+ return !!(pmovsr & ARMV8_PMU_CNT_MASK_ALL);
}
static int armv8pmu_counter_has_overflowed(u64 pmnc, int idx)
@@ -770,7 +770,7 @@ static u64 armv8pmu_getreset_flags(void)
value = read_pmovsclr();
/* Write to clear flags */
- value &= ARMV8_PMU_OVERFLOWED_MASK;
+ value &= ARMV8_PMU_CNT_MASK_ALL;
write_pmovsclr(value);
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)
/*
- * 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)
/*
* PMXEVTYPER: Event selection reg
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 03/20] KVM: arm64: Reorganize PMU functions
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
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 <coltonlewis@google.com>
---
arch/arm64/kvm/pmu-emul.c | 672 +------------------------------------
arch/arm64/kvm/pmu.c | 676 ++++++++++++++++++++++++++++++++++++++
include/kvm/arm_pmu.h | 7 +
3 files changed, 684 insertions(+), 671 deletions(-)
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 93cc9bbb5cecd..a40db0d5120ff 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -17,19 +17,10 @@
#define PERF_ATTR_CFG1_COUNTER_64BIT BIT(0)
-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);
-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_vcpu *vcpu, int cnt_idx)
return &vcpu->arch.pmu.pmc[cnt_idx];
}
-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 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1);
- u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0);
-
- return __kvm_pmu_event_mask(pmuver);
-}
-
-u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
-{
- u64 mask = ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 |
- kvm_pmu_event_mask(kvm);
-
- if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL2, IMP))
- mask |= ARMV8_PMU_INCLUDE_EL2;
-
- if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP))
- mask |= 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);
}
-static u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu)
-{
- unsigned int hpmn, n;
-
- if (!vcpu_has_nv(vcpu))
- return 0;
-
- hpmn = SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2));
- n = 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 >= n)
- return 0;
-
- /*
- * Programming HPMN=0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't
- * implemented. Since KVM's ability to emulate HPMN=0 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 = 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 = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));
-
- if (val == 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 *vcpu, 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 = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
@@ -393,24 +291,6 @@ static bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
return reg;
}
-static void kvm_pmu_update_state(struct kvm_vcpu *vcpu)
-{
- struct kvm_pmu *pmu = &vcpu->arch.pmu;
- bool overflow;
-
- overflow = kvm_pmu_overflow_status(vcpu);
- if (pmu->irq_level == overflow)
- return;
-
- pmu->irq_level = overflow;
-
- if (likely(irqchip_in_kernel(vcpu->kvm))) {
- int ret = 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 = &vcpu->arch.pmu;
@@ -436,43 +316,6 @@ void kvm_pmu_update_run(struct kvm_vcpu *vcpu)
regs->device_irq_level |= KVM_ARM_DEV_PMU;
}
-/**
- * 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 corresponding
- * 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 = 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);
}
-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 = kmalloc_obj(*entry);
- if (!entry)
- return;
-
- entry->arm_pmu = 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 = raw_smp_processor_id();
- list_for_each_entry(entry, &arm_pmus, entry) {
- pmu = 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 = __compute_pmceid(pmu, 0);
-
- /* always support SW_INCR */
- val |= BIT(ARMV8_PMUV3_PERFCTR_SW_INCR);
- /* always support CHAIN */
- val |= BIT(ARMV8_PMUV3_PERFCTR_CHAIN);
- return val;
-}
-
-static u64 compute_pmceid1(struct arm_pmu *pmu)
-{
- u64 val = __compute_pmceid(pmu, 1);
-
- /*
- * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled
- * as RAZ
- */
- val &= ~(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 = vcpu->kvm->arch.arm_pmu;
- unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
- u64 val, mask = 0;
- int base, i, nr_events;
-
- if (!pmceid1) {
- val = compute_pmceid0(cpu_pmu);
- base = 0;
- } else {
- val = compute_pmceid1(cpu_pmu);
- base = 32;
- }
-
- if (!bmap)
- return val;
-
- nr_events = kvm_pmu_event_mask(vcpu->kvm) + 1;
-
- for (i = 0; i < 32; i += 8) {
- u64 byte;
-
- byte = bitmap_get_value8(bmap, base + i);
- mask |= byte << i;
- if (nr_events >= (0x4000 + base + 32)) {
- byte = bitmap_get_value8(bmap, 0x4000 + base + i);
- mask |= byte << (32 + i);
- }
- }
-
- return val & mask;
-}
-
void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
{
u64 mask = kvm_pmu_implemented_counter_mask(vcpu);
@@ -921,393 +638,6 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
kvm_pmu_reprogram_counter_mask(vcpu, mask);
}
-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 = vcpu->arch.pmu.irq_num;
- /*
- * If we are using an in-kernel vgic, at this point we know
- * the vgic will be initialized, so we can check the PMU irq
- * number against the dimensions of the vgic and make sure
- * it's valid.
- */
- if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq))
- return -EINVAL;
- } else if (kvm_arm_pmu_irq_initialized(vcpu)) {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
-{
- if (irqchip_in_kernel(vcpu->kvm)) {
- int ret;
-
- /*
- * If using the PMU with an in-kernel virtual GIC
- * implementation, we require the GIC to be already
- * initialized when initializing the PMU.
- */
- if (!vgic_initialized(vcpu->kvm))
- return -ENODEV;
-
- if (!kvm_arm_pmu_irq_initialized(vcpu))
- return -ENXIO;
-
- ret = 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 = true;
- return 0;
-}
-
-/*
- * For one VM the interrupt type must be same for each vcpu.
- * As a PPI, the interrupt number is the same for all vcpus,
- * while as an SPI it must be a separate number per vcpu.
- */
-static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
-{
- unsigned long i;
- struct kvm_vcpu *vcpu;
-
- kvm_for_each_vcpu(i, vcpu, kvm) {
- if (!kvm_arm_pmu_irq_initialized(vcpu))
- continue;
-
- if (irq_is_ppi(irq)) {
- if (vcpu->arch.pmu.irq_num != irq)
- return false;
- } else {
- if (vcpu->arch.pmu.irq_num == 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 = 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 = 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 = __vcpu_sys_reg(vcpu, MDCR_EL2);
- val &= ~MDCR_EL2_HPMN;
- val |= 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 = 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 = 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 = vcpu->kvm;
- struct arm_pmu_entry *entry;
- struct arm_pmu *arm_pmu;
- int ret = -ENXIO;
-
- lockdep_assert_held(&kvm->arch.config_lock);
- mutex_lock(&arm_pmus_lock);
-
- list_for_each_entry(entry, &arm_pmus, entry) {
- arm_pmu = entry->arm_pmu;
- if (arm_pmu->pmu.type == pmu_id) {
- if (kvm_vm_has_ran_once(kvm) ||
- (kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
- ret = -EBUSY;
- break;
- }
-
- kvm_arm_set_pmu(kvm, arm_pmu);
- cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
- ret = 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 = 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 = 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 = (int __user *)(long)attr->addr;
- int irq;
-
- if (!irqchip_in_kernel(kvm))
- return -EINVAL;
-
- if (get_user(irq, uaddr))
- return -EFAULT;
-
- /* The PMU overflow interrupt can be a PPI or a valid SPI. */
- if (!(irq_is_ppi(irq) || irq_is_spi(irq)))
- return -EINVAL;
-
- if (!pmu_irq_is_valid(kvm, irq))
- return -EINVAL;
-
- if (kvm_arm_pmu_irq_initialized(vcpu))
- return -EBUSY;
-
- kvm_debug("Set kvm ARM PMU irq: %d\n", irq);
- vcpu->arch.pmu.irq_num = irq;
- return 0;
- }
- case KVM_ARM_VCPU_PMU_V3_FILTER: {
- u8 pmuver = 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 = __kvm_pmu_event_mask(pmuver) + 1;
-
- uaddr = (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 != KVM_PMU_EVENT_ALLOW &&
- filter.action != KVM_PMU_EVENT_DENY))
- return -EINVAL;
-
- if (kvm_vm_has_ran_once(kvm))
- return -EBUSY;
-
- if (!kvm->arch.pmu_filter) {
- kvm->arch.pmu_filter = 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 == KVM_PMU_EVENT_ALLOW)
- bitmap_zero(kvm->arch.pmu_filter, nr_events);
- else
- bitmap_fill(kvm->arch.pmu_filter, nr_events);
- }
-
- if (filter.action == 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 = (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 = (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 = (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 = 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 = 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 == 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 = __vcpu_sys_reg(vcpu, PMCR_EL0);
- u64 n = vcpu->kvm->arch.nr_pmu_counters;
-
- if (vcpu_has_nv(vcpu) && !vcpu_is_el2(vcpu))
- n = 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 = false;
diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c
index 6b48a3d16d0d5..ee2f0f7e61bcf 100644
--- a/arch/arm64/kvm/pmu.c
+++ b/arch/arm64/kvm/pmu.c
@@ -8,8 +8,22 @@
#include <linux/perf/arm_pmu.h>
#include <linux/perf/arm_pmuv3.h>
+#include <kvm/arm_pmu.h>
+
+#include <asm/kvm_emulate.h>
+
+static LIST_HEAD(arm_pmus);
+static DEFINE_MUTEX(arm_pmus_lock);
static DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events);
+#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
+
+bool kvm_supports_guest_pmuv3(void)
+{
+ guard(mutex)(&arm_pmus_lock);
+ return !list_empty(&arm_pmus);
+}
+
/*
* Given the perf event attributes and system type, determine
* if we are going to need to switch counters at guest entry/exit.
@@ -209,3 +223,665 @@ void kvm_vcpu_pmu_resync_el0(void)
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 = kmalloc_obj(*entry);
+ if (!entry)
+ return;
+
+ entry->arm_pmu = 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 = raw_smp_processor_id();
+ list_for_each_entry(entry, &arm_pmus, entry) {
+ pmu = 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 = __compute_pmceid(pmu, 0);
+
+ /* always support SW_INCR */
+ val |= BIT(ARMV8_PMUV3_PERFCTR_SW_INCR);
+ /* always support CHAIN */
+ val |= BIT(ARMV8_PMUV3_PERFCTR_CHAIN);
+ return val;
+}
+
+static u64 compute_pmceid1(struct arm_pmu *pmu)
+{
+ u64 val = __compute_pmceid(pmu, 1);
+
+ /*
+ * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled
+ * as RAZ
+ */
+ val &= ~(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 = vcpu->kvm->arch.arm_pmu;
+ unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
+ u64 val, mask = 0;
+ int base, i, nr_events;
+
+ if (!pmceid1) {
+ val = compute_pmceid0(cpu_pmu);
+ base = 0;
+ } else {
+ val = compute_pmceid1(cpu_pmu);
+ base = 32;
+ }
+
+ if (!bmap)
+ return val;
+
+ nr_events = kvm_pmu_event_mask(vcpu->kvm) + 1;
+
+ for (i = 0; i < 32; i += 8) {
+ u64 byte;
+
+ byte = bitmap_get_value8(bmap, base + i);
+ mask |= byte << i;
+ if (nr_events >= (0x4000 + base + 32)) {
+ byte = bitmap_get_value8(bmap, 0x4000 + base + i);
+ mask |= byte << (32 + i);
+ }
+ }
+
+ return val & mask;
+}
+
+/*
+ * When perf interrupt is an NMI, we cannot safely notify the vcpu corresponding
+ * 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 = 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 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64DFR0_EL1);
+ u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0);
+
+ return __kvm_pmu_event_mask(pmuver);
+}
+
+u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
+{
+ u64 mask = ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 |
+ kvm_pmu_event_mask(kvm);
+
+ if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL2, IMP))
+ mask |= ARMV8_PMU_INCLUDE_EL2;
+
+ if (kvm_has_feat(kvm, ID_AA64PFR0_EL1, EL3, IMP))
+ mask |= 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 = &vcpu->arch.pmu;
+ bool overflow;
+
+ overflow = kvm_pmu_overflow_status(vcpu);
+ if (pmu->irq_level == overflow)
+ return;
+
+ pmu->irq_level = overflow;
+
+ if (likely(irqchip_in_kernel(vcpu->kvm))) {
+ int ret = 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 = vcpu->arch.pmu.irq_num;
+ /*
+ * If we are using an in-kernel vgic, at this point we know
+ * the vgic will be initialized, so we can check the PMU irq
+ * number against the dimensions of the vgic and make sure
+ * it's valid.
+ */
+ if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq))
+ return -EINVAL;
+ } else if (kvm_arm_pmu_irq_initialized(vcpu)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
+{
+ if (irqchip_in_kernel(vcpu->kvm)) {
+ int ret;
+
+ /*
+ * If using the PMU with an in-kernel virtual GIC
+ * implementation, we require the GIC to be already
+ * initialized when initializing the PMU.
+ */
+ if (!vgic_initialized(vcpu->kvm))
+ return -ENODEV;
+
+ if (!kvm_arm_pmu_irq_initialized(vcpu))
+ return -ENXIO;
+
+ ret = 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 = true;
+ return 0;
+}
+
+/*
+ * For one VM the interrupt type must be same for each vcpu.
+ * As a PPI, the interrupt number is the same for all vcpus,
+ * while as an SPI it must be a separate number per vcpu.
+ */
+static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
+{
+ unsigned long i;
+ struct kvm_vcpu *vcpu;
+
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ if (!kvm_arm_pmu_irq_initialized(vcpu))
+ continue;
+
+ if (irq_is_ppi(irq)) {
+ if (vcpu->arch.pmu.irq_num != irq)
+ return false;
+ } else {
+ if (vcpu->arch.pmu.irq_num == 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 = 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 = 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 = __vcpu_sys_reg(vcpu, MDCR_EL2);
+
+ val &= ~MDCR_EL2_HPMN;
+ val |= 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 = 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 = 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 = vcpu->kvm;
+ struct arm_pmu_entry *entry;
+ struct arm_pmu *arm_pmu;
+ int ret = -ENXIO;
+
+ lockdep_assert_held(&kvm->arch.config_lock);
+ mutex_lock(&arm_pmus_lock);
+
+ list_for_each_entry(entry, &arm_pmus, entry) {
+ arm_pmu = entry->arm_pmu;
+ if (arm_pmu->pmu.type == pmu_id) {
+ if (kvm_vm_has_ran_once(kvm) ||
+ (kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
+ ret = -EBUSY;
+ break;
+ }
+
+ kvm_arm_set_pmu(kvm, arm_pmu);
+ cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
+ ret = 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 = 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 = 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 = (int __user *)(long)attr->addr;
+ int irq;
+
+ if (!irqchip_in_kernel(kvm))
+ return -EINVAL;
+
+ if (get_user(irq, uaddr))
+ return -EFAULT;
+
+ /* The PMU overflow interrupt can be a PPI or a valid SPI. */
+ if (!(irq_is_ppi(irq) || irq_is_spi(irq)))
+ return -EINVAL;
+
+ if (!pmu_irq_is_valid(kvm, irq))
+ return -EINVAL;
+
+ if (kvm_arm_pmu_irq_initialized(vcpu))
+ return -EBUSY;
+
+ kvm_debug("Set kvm ARM PMU irq: %d\n", irq);
+ vcpu->arch.pmu.irq_num = irq;
+ return 0;
+ }
+ case KVM_ARM_VCPU_PMU_V3_FILTER: {
+ u8 pmuver = 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 = __kvm_pmu_event_mask(pmuver) + 1;
+
+ uaddr = (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 != KVM_PMU_EVENT_ALLOW &&
+ filter.action != KVM_PMU_EVENT_DENY))
+ return -EINVAL;
+
+ if (kvm_vm_has_ran_once(kvm))
+ return -EBUSY;
+
+ if (!kvm->arch.pmu_filter) {
+ kvm->arch.pmu_filter = 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 == KVM_PMU_EVENT_ALLOW)
+ bitmap_zero(kvm->arch.pmu_filter, nr_events);
+ else
+ bitmap_fill(kvm->arch.pmu_filter, nr_events);
+ }
+
+ if (filter.action == 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 = (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 = (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 = (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 = 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 = 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 == 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 = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));
+
+ if (val == 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 = SYS_FIELD_GET(MDCR_EL2, HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2));
+ n = 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 >= n)
+ return 0;
+
+ /*
+ * Programming HPMN=0 is CONSTRAINED UNPREDICTABLE if FEAT_HPMN0 isn't
+ * implemented. Since KVM's ability to emulate HPMN=0 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 = 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 = __vcpu_sys_reg(vcpu, PMCR_EL0);
+ u64 n = vcpu->kvm->arch.nr_pmu_counters;
+
+ if (vcpu_has_nv(vcpu) && !vcpu_is_el2(vcpu))
+ n = FIELD_GET(MDCR_EL2_HPMN, __vcpu_sys_reg(vcpu, MDCR_EL2));
+
+ return u64_replace_bits(pmcr, n, ARMV8_PMU_PMCR_N);
+}
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index e91d15a7a564b..24a471cf59d56 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -53,13 +53,16 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, 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);
u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu);
+u64 kvm_pmu_hyp_counter_mask(struct kvm_vcpu *vcpu);
u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu);
+u32 kvm_pmu_event_mask(struct kvm *kvm);
u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1);
void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu);
void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu);
void kvm_pmu_reprogram_counter_mask(struct kvm_vcpu *vcpu, u64 val);
void kvm_pmu_flush_hwstate(struct kvm_vcpu *vcpu);
void kvm_pmu_sync_hwstate(struct kvm_vcpu *vcpu);
+bool kvm_pmu_overflow_status(struct kvm_vcpu *vcpu);
bool kvm_pmu_should_notify_user(struct kvm_vcpu *vcpu);
void kvm_pmu_update_run(struct kvm_vcpu *vcpu);
void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val);
@@ -132,6 +135,10 @@ static inline u64 kvm_pmu_accessible_counter_mask(struct 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, u64 val) {}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 02/20] KVM: arm64: Reorganize PMU includes
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
From: Marc Zyngier <maz@kernel.org>
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 <maz@kernel.org>
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
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/arm_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
-#include <asm/kvm_host.h>
-
#include <asm/cpufeature.h>
#include <asm/sysreg.h>
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 70cb9cfd760a3..1f789ba589d56 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1441,25 +1441,11 @@ void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu);
void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu);
void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu);
-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 *attr) {}
-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_guest) {}
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 @@
*/
#include <asm/irq_regs.h>
+
+#if defined(CONFIG_ARM64)
+#include <kvm/arm_pmu.h>
+#endif
+
#include <asm/perf_event.h>
#include <asm/virt.h>
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 96754b51b4116..e91d15a7a564b 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -9,9 +9,19 @@
#include <linux/perf_event.h>
#include <linux/perf/arm_pmuv3.h>
+#include <linux/perf/arm_pmu.h>
#define KVM_ARMV8_PMU_MAX_COUNTERS 32
+#define kvm_pmu_counter_deferred(attr) \
+ ({ \
+ !has_vhe() && (attr)->exclude_host; \
+ })
+
+struct kvm;
+struct kvm_device_attr;
+struct kvm_vcpu;
+
#if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM)
struct kvm_pmc {
u8 idx; /* index into the pmu->pmc array */
@@ -66,6 +76,9 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu,
int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu);
struct kvm_pmu_events *kvm_get_pmu_events(void);
+void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr);
+void kvm_clr_pmu_events(u64 clr);
+bool kvm_set_pmuserenr(u64 val);
void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu);
void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
void kvm_vcpu_pmu_resync_el0(void);
@@ -159,6 +172,12 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
#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 *attr) {}
+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) {}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 01/20] arm64: cpufeature: Add cpucap for HPMN0
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
In-Reply-To: <20260504211813.1804997-1-coltonlewis@google.com>
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 <mark.rutland@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
arch/arm64/kernel/cpufeature.c | 8 ++++++++
arch/arm64/kvm/sys_regs.c | 3 ++-
arch/arm64/tools/cpucaps | 1 +
arch/arm64/tools/sysreg | 6 +++---
4 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32c2dbcc0c641..5c6c76a9696cc 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -556,6 +556,7 @@ static const struct arm64_ftr_bits ftr_id_mmfr0[] = {
};
static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
+ 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_CTX_CMPs_SHIFT, 4, 0),
@@ -2964,6 +2965,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.matches = has_cpuid_feature,
ARM64_CPUID_FIELDS(ID_AA64MMFR0_EL1, FGT, FGT2)
},
+ {
+ .desc = "HPMN0",
+ .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .capability = ARM64_HAS_HPMN0,
+ .matches = has_cpuid_feature,
+ ARM64_CPUID_FIELDS(ID_AA64DFR0_EL1, HPMN0, IMP)
+ },
#ifdef CONFIG_ARM64_SME
{
.desc = "Scalable Matrix Extension",
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 1b4cacb6e918a..0a8e8ee69cd00 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3232,7 +3232,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
ID_AA64DFR0_EL1_DoubleLock_MASK |
ID_AA64DFR0_EL1_WRPs_MASK |
ID_AA64DFR0_EL1_PMUVer_MASK |
- ID_AA64DFR0_EL1_DebugVer_MASK),
+ ID_AA64DFR0_EL1_DebugVer_MASK |
+ ID_AA64DFR0_EL1_HPMN0_MASK),
ID_SANITISED(ID_AA64DFR1_EL1),
ID_UNALLOCATED(5,2),
ID_UNALLOCATED(5,3),
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 7261553b644b2..654b165781854 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -42,6 +42,7 @@ HAS_GIC_PRIO_MASKING
HAS_GIC_PRIO_RELAXED_SYNC
HAS_ICH_HCR_EL2_TDIR
HAS_HCR_NV1
+HAS_HPMN0
HAS_HCX
HAS_LDAPR
HAS_LPA2
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 9d1c211080571..92135f8834be0 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -1666,9 +1666,9 @@ EndEnum
EndSysreg
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
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v7 00/20] ARM64 PMU Partitioning
From: Colton Lewis @ 2026-05-04 21:17 UTC (permalink / raw)
To: kvm
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, linux-kernel, linux-arm-kernel, kvmarm,
linux-perf-users, linux-kselftest, Colton Lewis
This series creates a new PMU scheme on ARM, a partitioned PMU that
allows reserving a subset of counters for more direct guest access,
significantly reducing overhead. More details, including performance
benchmarks, can be read in the v1 cover letter linked below.
An overview of what this series accomplishes was presented at KVM
Forum 2025. Slides [1] and video [2] are linked below.
After a few false starts, meeting with Will Deacon and Mark Rutland to
discuss implementation ideas, and a few more false starts, I finally
have an implementation of dynamic counter reservation that works
without disrupting host perf too much. Now the host only loses access
to the guest counters when a vCPU resides on the CPU.
The key was creating perf_pmu_resched_update, which behaves exactly
like perf_pmu_resched except it takes a callback to call in between
when the perf events are scheduled out and when they are scheduled
back in. That allows us to update the PMU's available counters when we
know they are not currently in use without needing to expose private
perf core functions and triple check they are not being called in a
way that violates existing assumptions.
Because this introduces a possibility of perf reschedule during vCPU
load, I've optimized to only do that operation if there are host
events occupying the intended guest counters at the time of the load.
The kernel command line parameter for the driver still exists, but now
only defines an upper limit of counters the guest might use rather
than taking those counters from the host permanently.
v7:
* Implement dynamic counter reservation as described above. One side
effect is the PMUv3 driver now needs much fewer changes to enforce
the boundary.
* Move register accesses out of fast path for non-FGT hardware. The
performance impact was negligible and this moves bloat out of the
fast path and allows a more reliable design with more code sharing.
* Make PMCCNTR a special case in the context swap again because trying
to access it with PMXEVCNTR is undefined.
* Fix a bug where kvm_pmu_guest_counter_mask was using & instead of |.
* Re-expose the dedicated instruction counter to the host since it was
decided the guest will not own it.
* Change the global armv8pmu_reserved_host_counters to
armv8pmu_is_partitoned because it was only used in boolean checks.
* Fix typo in vcpu attribute commit so the spelling of the flag in the
commit message matches the code.
* Rebase to v7.0-rc7
v6:
https://lore.kernel.org/kvmarm/20260209221414.2169465-1-coltonlewis@google.com/
v5:
https://lore.kernel.org/kvmarm/20251209205121.1871534-1-coltonlewis@google.com/
v4:
https://lore.kernel.org/kvmarm/20250714225917.1396543-1-coltonlewis@google.com/
v3:
https://lore.kernel.org/kvm/20250626200459.1153955-1-coltonlewis@google.com/
v2:
https://lore.kernel.org/kvm/20250620221326.1261128-1-coltonlewis@google.com/
v1:
https://lore.kernel.org/kvm/20250602192702.2125115-1-coltonlewis@google.com/
[1] https://gitlab.com/qemu-project/kvm-forum/-/raw/main/_attachments/2025/Optimizing__itvHkhc.pdf
[2] https://www.youtube.com/watch?v=YRzZ8jMIA6M&list=PLW3ep1uCIRfxwmllXTOA2txfDWN6vUOHp&index=9
Colton Lewis (19):
arm64: cpufeature: Add cpucap for HPMN0
KVM: arm64: Reorganize PMU functions
perf: arm_pmuv3: Generalize counter bitmasks
perf: arm_pmuv3: Check cntr_mask before using pmccntr
perf: arm_pmuv3: Add method to partition the PMU
KVM: arm64: Set up FGT for Partitioned PMU
KVM: arm64: Add Partitioned PMU register trap handlers
KVM: arm64: Set up MDCR_EL2 to handle a Partitioned PMU
KVM: arm64: Context swap Partitioned PMU guest registers
KVM: arm64: Enforce PMU event filter at vcpu_load()
perf: Add perf_pmu_resched_update()
KVM: arm64: Apply dynamic guest counter reservations
KVM: arm64: Implement lazy PMU context swaps
perf: arm_pmuv3: Handle IRQs for Partitioned PMU guest counters
KVM: arm64: Detect overflows for the Partitioned PMU
KVM: arm64: Add vCPU device attr to partition the PMU
KVM: selftests: Add find_bit to KVM library
KVM: arm64: selftests: Add test case for Partitioned PMU
KVM: arm64: selftests: Relax testing for exceptions when partitioned
Marc Zyngier (1):
KVM: arm64: Reorganize PMU includes
arch/arm/include/asm/arm_pmuv3.h | 18 +
arch/arm64/include/asm/arm_pmuv3.h | 12 +-
arch/arm64/include/asm/kvm_host.h | 17 +-
arch/arm64/include/asm/kvm_types.h | 6 +-
arch/arm64/include/uapi/asm/kvm.h | 2 +
arch/arm64/kernel/cpufeature.c | 8 +
arch/arm64/kvm/Makefile | 2 +-
arch/arm64/kvm/arm.c | 2 +
arch/arm64/kvm/config.c | 41 +-
arch/arm64/kvm/debug.c | 31 +-
arch/arm64/kvm/pmu-direct.c | 494 ++++++++++++
arch/arm64/kvm/pmu-emul.c | 674 +----------------
arch/arm64/kvm/pmu.c | 701 ++++++++++++++++++
arch/arm64/kvm/sys_regs.c | 250 ++++++-
arch/arm64/tools/cpucaps | 1 +
arch/arm64/tools/sysreg | 6 +-
drivers/perf/arm_pmuv3.c | 111 ++-
include/kvm/arm_pmu.h | 110 +++
include/linux/perf/arm_pmu.h | 3 +
include/linux/perf/arm_pmuv3.h | 14 +-
include/linux/perf_event.h | 3 +
kernel/events/core.c | 28 +-
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/arm64/vpmu_counter_access.c | 112 ++-
tools/testing/selftests/kvm/lib/find_bit.c | 1 +
25 files changed, 1861 insertions(+), 787 deletions(-)
create mode 100644 arch/arm64/kvm/pmu-direct.c
create mode 100644 tools/testing/selftests/kvm/lib/find_bit.c
base-commit: 591cd656a1bf5ea94a222af5ef2ee76df029c1d2
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply
* Re: [PATCH 0/9] Improve process/maintainers output
From: Randy Dunlap @ 2026-05-04 21:13 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Jonathan Corbet, Mauro Carvalho Chehab,
Miguel Ojeda
Cc: linux-doc, linux-kernel, rust-for-linux, Björn Roy Baron,
Alice Ryhl, Andreas Hindborg, Andrew Morton, Benno Lossin,
Boqun Feng, Danilo Krummrich, Gary Guo, Joe Perches, Matteo Croce,
Shuah Khan, Trevor Gross
In-Reply-To: <cover.1777908711.git.mchehab+huawei@kernel.org>
On 5/4/26 8:51 AM, Mauro Carvalho Chehab wrote:
> Hi Jon,
>
> As promised, this series improve the output at process/maintainers:
> instead of a pure enriched text, the maintainer's file content is
> now converted with a table, and has gained a javascript to allow
> filtering entries.
>
> The initial patches change the logic to split parsing from
> output generation. Now, everything is stored into a dict at
> the parsing phase. This way, it is easier to adjust the
> directive handler for it to produce a more structured document.
>
> Right now, the entries are sorted alphabetically, per subsystem's
> name.
How is subsystem determined? Just by the heading?
The MAINTAINERS file doesn't stay sorted so the output isn't sorted
unless I am just missing something basic.
See e.g.:
DRM TTM SUBSYSTEM
GPU BUDDY ALLOCATOR
DRM AUTOMATED TESTING
--
~Randy
^ permalink raw reply
* Re: [PATCH 9/9] docs: maintainers: add a filtering javascript
From: Randy Dunlap @ 2026-05-04 21:12 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Jonathan Corbet, Linux Doc Mailing List,
Mauro Carvalho Chehab
Cc: linux-kernel, rust-for-linux, Shuah Khan
In-Reply-To: <854faf4127053c203cae479f68e6ac12a4e4aabc.1777908711.git.mchehab+huawei@kernel.org>
On 5/4/26 8:51 AM, Mauro Carvalho Chehab wrote:
> The maintainers table is big. Add a javascript to allow filtering
> it. Such script is only added at the page which contains the
> maintainers-include tag.
>
> I opted to keep the search case-sensitive, as, this way,
> upper case searches at subsystem.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> ---
> Documentation/sphinx/maintainers_include.py | 77 +++++++++++++++++++--
> 1 file changed, 71 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
> index bbdadf2aa4f3..f85298627da2 100755
> --- a/Documentation/sphinx/maintainers_include.py
> +++ b/Documentation/sphinx/maintainers_include.py
> + function addInput() {
> + const table = document.getElementById("maintainers-table");
> + if (!table) return;
> + let input = document.getElementById("filter-table");
> + if (!input) {
> + const filt_div = document.createElement('div');
> + filt_div.innerHTML = `
> + <p>Filter:
> + <input type="search" id="filter-table" placeholder="subsystem or property (case-sensitive)" />
The "placeholder" string does not fit inside the prompt/entry block so it is
truncated with no way to tell what the missing part is AFAICT.
Maybe include that after "Filter:"? E.g.,
<p>Filter: (subsystem or property, case-sensitive)
> + </p>
> + `;
--
~Randy
^ permalink raw reply
* Re: [PATCH] docs: cgroup-v1: Update charge-commit section
From: Tejun Heo @ 2026-05-04 21:05 UTC (permalink / raw)
To: T.J. Mercier
Cc: Johannes Weiner, Michal Koutný, cgroups, Jonathan Corbet,
Shuah Khan, linux-doc, linux-kernel
In-Reply-To: <20260430201142.240387-1-tjmercier@google.com>
Hello,
Applied to cgroup/for-7.1-fixes.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH] docs: kselftest: Document the FORCE_TARGETS build variable
From: Shuah Khan @ 2026-05-04 20:58 UTC (permalink / raw)
To: Ricardo B. Marlière, Shuah Khan, Jonathan Corbet
Cc: linux-kselftest, workflows, linux-doc, linux-kernel, Shuah Khan
In-Reply-To: <20260417-selftests-docs-v1-1-32e4a78214eb@suse.com>
On 4/17/26 11:36, Ricardo B. Marlière wrote:
> FORCE_TARGETS has been part of the kselftest build system for
> some time but is absent from the developer documentation. Without
> an entry here, users relying on kselftest in CI pipelines would
> have to read the selftests Makefile directly to discover the
> option.
>
> A build that exits zero despite some targets failing can mask
> real breakage and mislead automated systems into reporting
> success. Add a dedicated section so that CI authors can easily
> find and adopt FORCE_TARGETS=1 to turn such silent partial
> failures into hard errors.
>
> Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
> ---
>
Thanks Ricardo. I applied it to linux-kselftest next branch.
thanks,
-- Shuah
^ permalink raw reply
* Re: [PATCH 1/2] Documentation: maple_tree: Point out constraint when using xa_{mk, to}_value
From: Matthew Wilcox @ 2026-05-04 20:32 UTC (permalink / raw)
To: Wei-Lin Chang
Cc: maple-tree, linux-mm, linux-doc, linux-kernel, Liam R . Howlett,
Alice Ryhl, Andrew Ballance, Jonathan Corbet, Shuah Khan
In-Reply-To: <20260504165746.1422057-2-weilin.chang@arm.com>
On Mon, May 04, 2026 at 05:57:45PM +0100, Wei-Lin Chang wrote:
> Using xa_{mk, to}_value when storing values loses the information of
> the top bit from the left shift, point that out in the doc.
I don't know if that's necessary ... it's obvious when looking at the
function:
static inline void *xa_mk_value(unsigned long v)
{
WARN_ON((long)v < 0);
return (void *)((v << 1) | 1);
}
and if you ignore it, you'll find out. But if this needs to be
documented anywhere, it's in the kernel-doc for xa_mk_value()
and not in the maple tree docs.
^ permalink raw reply
* Re: [PATCH 8/9] docs: maintainers_include: don't ignore invalid profile entries
From: Mauro Carvalho Chehab @ 2026-05-04 20:26 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab,
linux-kernel, rust-for-linux, Björn Roy Baron, Alice Ryhl,
Andreas Hindborg, Benno Lossin, Boqun Feng, Danilo Krummrich,
Gary Guo, Miguel Ojeda, Shuah Khan, Trevor Gross
In-Reply-To: <CANiq72n+y0AerfiUzh5fLpMRiGGFq5rMxqweHG-TsmX_05vxBA@mail.gmail.com>
On Mon, 4 May 2026 18:08:06 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:
> On Mon, May 4, 2026 at 5:51 PM Mauro Carvalho Chehab
> <mchehab+huawei@kernel.org> wrote:
> >
> > Currently, there is a "P" entry for Rust pin-point that is
> > neither a valid ReST file nor an hyperlink. While the real
>
> I guess you mean pin-init above, i.e. this entry:
>
> P: rust/pin-init/CONTRIBUTING.md
>
> It would be nice to clarify it in the commit message that it refers to
> a file (which is allowed for `P:` entries according to the docs).
It is not written there, but by file, it would actually be expected
a file within Documentation in ReST format ;-)
> And, yeah, ideally we could make it a hyperlink to the raw file.
I'm afraid that this is not possible. Sphinx doesn't allow
hyperlinks to point to files outside the documentation root
(which is Documentation/ when SPHINXDIRS is not used).
IMO the best would be to run:
pandoc -i rust/pin-init/CONTRIBUTING.md -t rst -o Documentation/rust/pin-init-profile.rst
sed s,rust/pin-init/CONTRIBUTING.md,Documentation/rust/pin-init-profile.rst, -i MAINTAINERS
This way, it will generate a proper hyperlink.
Thanks,
Mauro
^ permalink raw reply
* Re: [PATCH v12 12/22] gpu: nova-core: mm: Add page table entry operation traits
From: Joel Fernandes @ 2026-05-04 19:50 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alexandre Courbot, linux-kernel, Miguel Ojeda, Boqun Feng,
Gary Guo, Bjorn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Dave Airlie, Daniel Almeida, dri-devel,
rust-for-linux, nova-gpu, Nikola Djukic, David Airlie, Boqun Feng,
John Hubbard, Alistair Popple, Timur Tabi, Edwin Peer,
Andrea Righi, Andy Ritger, Zhi Wang, Balbir Singh,
Philipp Stanner, alexeyi, Eliot Courtney, joel, linux-doc
In-Reply-To: <DIA5D0RYRE24.2LB65BF0UK4UI@kernel.org>
On 5/4/2026 3:42 PM, Danilo Krummrich wrote:
> On Mon May 4, 2026 at 9:28 PM CEST, Joel Fernandes wrote:
>> We are already at v12 now
>
> To be fair, the series is actually at v6, as you initially added those patches
> to another series that was at v6 already back then, and then you decided to just
> keep going with it. So, nothing too crazy going on that front. :-)
I was thinking of resetting it to v1 and starting by breaking it up and
starting over from what's left instead of a v13. I think at this point,
probably breaking it down into multiple series with new version numbers
makes sense. And I can point to the old patch series that way for reference.
Could you clarify how much time we have left to submit to drm-rust-next? I
can then plan the next series accordingly. I did make some changes to GpuMm
to be more suitable for channels, but haven't posted them yet.
^ permalink raw reply
* Re: [PATCH v12 12/22] gpu: nova-core: mm: Add page table entry operation traits
From: Danilo Krummrich @ 2026-05-04 19:42 UTC (permalink / raw)
To: Joel Fernandes
Cc: Alexandre Courbot, linux-kernel, Miguel Ojeda, Boqun Feng,
Gary Guo, Bjorn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Dave Airlie, Daniel Almeida, dri-devel,
rust-for-linux, nova-gpu, Nikola Djukic, David Airlie, Boqun Feng,
John Hubbard, Alistair Popple, Timur Tabi, Edwin Peer,
Andrea Righi, Andy Ritger, Zhi Wang, Balbir Singh,
Philipp Stanner, alexeyi, Eliot Courtney, joel, linux-doc
In-Reply-To: <bd210abc-590f-4011-8337-21b54780fd4c@nvidia.com>
On Mon May 4, 2026 at 9:28 PM CEST, Joel Fernandes wrote:
> We are already at v12 now
To be fair, the series is actually at v6, as you initially added those patches
to another series that was at v6 already back then, and then you decided to just
keep going with it. So, nothing too crazy going on that front. :-)
^ permalink raw reply
* Re: [PATCH v12 12/22] gpu: nova-core: mm: Add page table entry operation traits
From: Joel Fernandes @ 2026-05-04 19:28 UTC (permalink / raw)
To: Alexandre Courbot
Cc: linux-kernel, Miguel Ojeda, Boqun Feng, Gary Guo, Bjorn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Dave Airlie, Daniel Almeida, dri-devel,
rust-for-linux, nova-gpu, Nikola Djukic, David Airlie, Boqun Feng,
John Hubbard, Alistair Popple, Timur Tabi, Edwin Peer,
Andrea Righi, Andy Ritger, Zhi Wang, Balbir Singh,
Philipp Stanner, alexeyi, Eliot Courtney, joel, linux-doc
In-Reply-To: <DI9YR10HH6PE.AMLEZUV7701V@nvidia.com>
On 5/4/2026 10:31 AM, Alexandre Courbot wrote:
> On Sun May 3, 2026 at 4:19 AM JST, Joel Fernandes wrote:
>>> Please reorder things so they land, as much as possible, in their final
>>> form. In this case this probably means defining the trait *before* the V2
>>> and V3 page table definitions, so they can implement it from the get-go.
>>
>> That is a reasonable approach too, I can try to do that, but it is
>> misleading to say '270 lines of diff that reviewers will have processed for
>> nothing' which is nothing but fiction. Please look more carefully, the
>> patch is iterative on the series.
>
> For context, here is where the 270 lines of diff come from:
>
> drivers/gpu/nova-core/mm/pagetable/ver2.rs | 150 ++++++++------
> drivers/gpu/nova-core/mm/pagetable/ver3.rs | 120 +++++++----
>
> But the number is not important.
It is important, numbers and accuracy are really important things
especially on the Linux kernel mailing list. Sorry if you feel that is
inconvenient. And even quoting the 270 is a falsehood, the 270 lines were
not refactored, only 90 lines or so was.
> My feedback was that refactoring code> right after you introduce it is a
strong indicator that something should
> be reordered/squashed to prevent unneeded cognitive load on reviewers.
> This is particularly important on long series like this one.
Again you are misleading in the email, because the scale of 'refactor'
matters. Only 90 lines were 'refactored' with 300 more lines added so it is
a perfectly reasonable approach to refactor a small part of an earlier
patch when you're trying to introduce a new concept (in this case, we
introduced new traits). Do you want me to show you examples of that in the
git tree or are you convinced?
Considering the amount of change is so small, I would prefer to leave it in
its current state since I don't see the benefit of further restructuring.
We are already at v12 now and the patches were posted and reposted (and
tested) now for months. Keep in mind also that I already re-ordered the
patches once per your request, and I don't think I would do that yet again.
We are getting close to merging at this point.
thanks,
--
Joel Fernandes
^ permalink raw reply
* Re: [PATCH v13 1/4] docs: driver-api: gpio: rpmsg gpio driver over rpmsg bus
From: Mathieu Poirier @ 2026-05-04 19:23 UTC (permalink / raw)
To: Shenwei Wang
Cc: Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Frank Li,
Sascha Hauer, Shuah Khan, linux-gpio, linux-doc, linux-kernel,
Pengutronix Kernel Team, Fabio Estevam, Peng Fan, devicetree,
linux-remoteproc, imx, linux-arm-kernel, linux-imx
In-Reply-To: <20260422212849.1240591-2-shenwei.wang@nxp.com>
On Wed, Apr 22, 2026 at 04:28:46PM -0500, Shenwei Wang wrote:
> Describes the gpio rpmsg transport protocol over the rpmsg bus between
> the remote system and Linux.
>
> Signed-off-by: Shenwei Wang <shenwei.wang@nxp.com>
> ---
> Documentation/driver-api/gpio/gpio-rpmsg.rst | 266 +++++++++++++++++++
> Documentation/driver-api/gpio/index.rst | 1 +
> 2 files changed, 267 insertions(+)
> create mode 100644 Documentation/driver-api/gpio/gpio-rpmsg.rst
>
> diff --git a/Documentation/driver-api/gpio/gpio-rpmsg.rst b/Documentation/driver-api/gpio/gpio-rpmsg.rst
> new file mode 100644
> index 000000000000..abfde68c9b0a
> --- /dev/null
> +++ b/Documentation/driver-api/gpio/gpio-rpmsg.rst
> @@ -0,0 +1,266 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +GPIO RPMSG (Remote Processor Messaging) Protocol
> +================================================
> +
> +The GPIO RPMSG transport protocol is used for communication and interaction
> +with GPIO controllers on remote processors via the RPMSG bus.
> +
> +Message Format
> +--------------
> +
> +The RPMSG message consists of a 6-byte packet with the following layout:
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + |type |cmd |port |line | data |
> + +-----+-----+-----+-----+-----+----+
I will take a final decision on the 'port' field when I'm done reading the
thread on how endpoints should be created.
> +
> +- **type (Message Type)**: The message type can be one of:
> +
> + - 0: GPIO_RPMSG_SEND
> + - 1: GPIO_RPMSG_REPLY
> + - 2: GPIO_RPMSG_NOTIFY
> +
> +- **cmd**: Command code, used for GPIO_RPMSG_SEND messages.
> +
> +- **port**: The GPIO port (bank) index.
> +
> +- **line**: The GPIO line (pin) index of the port.
> +
> +- **data**: See details in the command description below.
> +
> +- **reply err**: Error code from the remote core.
> +
> + - 0: Success
> + - 1: General error (Early remote software only returns this unclassified error)
> + - 2: Not supported (A command is not supported by the remote firmware)
> + - 3: Resource not available (The resource is not allocated to Linux)
> + - 4: Resource busy (The resource is already in use)
> + - 5: Parameter error
No. The virtio-GPIO specification does not define any of these. We are not
re-inventing the specification, we are only using it on top of RPMSG. The only
value for 'status' are VIRTIO_GPIO_STATUS_OK and VIRTIO_GPIO_STATUS_ERR. Modify
the virtio-GPIO specification if you want to do something like this.
> +
> +
> +GPIO Commands
> +-------------
> +
> +Commands are specified in the **Cmd** field for **GPIO_RPMSG_SEND** (Type=0) messages.
> +
> +The SEND message is always sent from Linux to the remote firmware. Each
> +SEND corresponds to a single REPLY message. The GPIO driver should
> +serialize messages and determine whether a REPLY message is required. If a
> +REPLY message is expected but not received within the specified timeout
> +period (currently 1 second in the Linux driver), the driver should return
> +-ETIMEOUT.
> +
> +GET_DIRECTION (Cmd=2)
> +~~~~~~~~~~~~~~~~~~~~~
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 2 |port |line | 0 | 0 |
> + +-----+-----+-----+-----+-----+----+
'line' should be 16 bit followed by a 32 bit value.
> +
> +**Reply:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 1 | 2 |port |line | err | dir|
> + +-----+-----+-----+-----+-----+----+
Same as above, 'line' should be 16 bit. 'err' should be 'status' and 'dir'
should be 'value'.
> +
> +- **err**: See above for definitions.
> +
> +- **dir**: Direction.
> +
> + - 0: None
> + - 1: Output
> + - 2: Input
> +
> +SET_DIRECTION (Cmd=3)
> +~~~~~~~~~~~~~~~~~~~~~
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 3 |port |line | dir | 0 |
> + +-----+-----+-----+-----+-----+----+
Same as above, i.e 'line' is 16 bit follow by a 32 bit value.
> +
> +- **dir**: Direction.
> +
> + - 0: None
> + - 1: Output
> + - 2: Input
> +
> +**Reply:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 1 | 3 |port |line | err | 0 |
> + +-----+-----+-----+-----+-----+----+
Same as my reply for GET_DIRECTION.
The same for all the other messages below. There should be a direct and obvious
connection between this protocol and virtio-gpio. In fact I'm starting to
wonder if we should have two endpoints per GPIO controller, one for the requestq
and another one for the eventq. That would make this entire protocol
unecessary. I will elaborate more later when I read Beleswar and Arnaud's
conversation on that topic.
> +
> +- **err**: See above for definitions.
> +
> +
> +GET_VALUE (Cmd=4)
> +~~~~~~~~~~~~~~~~~
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 4 |port |line | 0 | 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +**Reply:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 1 | 4 |port |line | err | val|
> + +-----+-----+-----+-----+-----+----+
> +
> +- **err**: See above for definitions.
> +
> +- **val**: Line level.
> +
> + - 0: Low
> + - 1: High
> +
> +SET_VALUE (Cmd=5)
> +~~~~~~~~~~~~~~~~~
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 5 |port |line | val | 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **val**: Output level.
> +
> + - 0: Low
> + - 1: High
> +
> +**Reply:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 1 | 5 |port |line | err | 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **err**: See above for definitions.
> +
> +SET_IRQ_TYPE (Cmd=6)
> +~~~~~~~~~~~~~~~~~~~~
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 6 |port |line | val | wk |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **val**: IRQ types.
> +
> + - 0: Interrupt disabled
> + - 1: Rising edge trigger
> + - 2: Falling edge trigger
> + - 3: Both edge trigger
> + - 4: High level trigger
> + - 8: Low level trigger
> +
> +- **wk**: Wakeup enable.
> +
> + The remote system should always aim to stay in a power-efficient state by
> + shutting down or clock-gating the GPIO blocks that aren't in use. Since
> + the remoteproc driver is responsible for managing the power states of the
> + remote firmware, the GPIO driver does not require to know the firmware's
> + running states.
> +
> + When the wakeup bit is set, the remote firmware should configure the line
> + as a wakeup source. The firmware should send the notification message to
> + Linux after it is woken from the GPIO line.
> +
> + - 0: Disable wakeup from GPIO
> + - 1: Enable wakeup from GPIO
This is not part of the virtio-GPIO specification. Again, modify the
virtio-GPIO specification to do this.
> +
> +**Reply:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 1 | 6 |port |line | err | 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **err**: See above for definitions.
> +
> +NOTIFY_REPLY (Cmd=10)
> +~~~~~~~~~~~~~~~~~~~~~
> +The reply message for the notification is optional. The remote firmware can
> +implement it to simulate the interrupt acknowledgment behavior.
> +
> +**Request:**
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 0 | 10 |port |line |level| 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **port**: The GPIO port (bank) index.
> +
> +- **line**: The GPIO line (pin) index of the port.
> +
> +- **level**: GPIO line status.
No. In accordance with the specification, the only thing a device sends when an
interrupt occurs is VIRTIO_GPIO_IRQ_STATUS_VALID, or
VIRTIO_GPIO_IRQ_STATUS_INVALID to return the buffers back to the driver when
interrupts are disabled.
> +
> +Notification Message
> +--------------------
> +
> +Notifications are sent by the remote core and they have
> +**Type=2 (GPIO_RPMSG_NOTIFY)**:
> +
No. Once again, we are not re-writing the specification. Virtio-GPIO doesn't
need this so I don't see whey virtio-rpmsg-gpio would.
> +When a GPIO line asserts an interrupt on the remote processor, the firmware
> +should immediately mask the corresponding interrupt source and send a
> +notification message to the Linux. Upon completion of the interrupt
> +handling on the Linux side, the driver should issue a
> +command **SET_IRQ_TYPE** to the firmware to unmask the interrupt.
> +
> +A Notification message can arrive between a SEND and its REPLY message,
> +and the driver is expected to handle this scenario.
> +
> +.. code-block:: none
> +
> + +-----+-----+-----+-----+-----+----+
> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> + | 2 | 0 |port |line |type | 0 |
> + +-----+-----+-----+-----+-----+----+
> +
> +- **port**: The GPIO port (bank) index.
> +
> +- **line**: The GPIO line (pin) index of the port.
> +
> +- **type**: Optional parameter to indicate the trigger event type.
> +
> diff --git a/Documentation/driver-api/gpio/index.rst b/Documentation/driver-api/gpio/index.rst
> index bee58f709b9a..e5eb1f82f01f 100644
> --- a/Documentation/driver-api/gpio/index.rst
> +++ b/Documentation/driver-api/gpio/index.rst
> @@ -16,6 +16,7 @@ Contents:
> drivers-on-gpio
> bt8xxgpio
> pca953x
> + gpio-rpmsg
>
> Core
> ====
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Shah, Tanmay @ 2026-05-04 19:19 UTC (permalink / raw)
To: Arnaud POULIQUEN, Beleswar Prasad Padhi, Mathieu Poirier
Cc: Shenwei Wang, Andrew Lunn, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Frank Li, Sascha Hauer, Shuah Khan,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, dl-linux-imx,
Bartosz Golaszewski
In-Reply-To: <4c526816-b127-43e7-86e9-eee4dc1152bc@foss.st.com>
Hello all,
I have started reviewing this work as well.
Thanks Shenwei for this work.
I have gone through only the current revision, and would like to provide
idea on how to achieve GPIO number multiplexing with the RPMsg protocol.
Also, have some bindings related question.
Please see below:
On 4/30/2026 11:40 AM, Arnaud POULIQUEN wrote:
>
>
> On 4/30/26 14:56, Beleswar Prasad Padhi wrote:
>> Hello Arnaud,
>>
>> On 30/04/26 13:05, Arnaud POULIQUEN wrote:
>>> Hello,
>>>
>>> On 4/29/26 21:20, Mathieu Poirier wrote:
>>>> On Wed, 29 Apr 2026 at 12:07, Padhi, Beleswar <b-padhi@ti.com> wrote:
>>>>>
>>>>> Hi Mathieu,
>>>>>
>>>>> On 4/29/2026 11:03 PM, Mathieu Poirier wrote:
>>>>>> On Wed, 29 Apr 2026 at 10:53, Shenwei Wang <shenwei.wang@nxp.com>
>>>>>> wrote:
>>>>>>>
>>>>>>>
>>>>>>>> -----Original Message-----
>>>>>>>> From: Mathieu Poirier <mathieu.poirier@linaro.org>
>>>>>>>> Sent: Wednesday, April 29, 2026 10:42 AM
>>>>>>>> To: Shenwei Wang <shenwei.wang@nxp.com>
>>>>>>>> Cc: Andrew Lunn <andrew@lunn.ch>; Padhi, Beleswar <b-
>>>>>>>> padhi@ti.com>; Linus
>>>>>>>> Walleij <linusw@kernel.org>; Bartosz Golaszewski
>>>>>>>> <brgl@kernel.org>; Jonathan
>>>>>>>> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>;
>>>>>>>> Krzysztof Kozlowski
>>>>>>>> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Bjorn
>>>>>>>> Andersson
>>>>>>>> <andersson@kernel.org>; Frank Li <frank.li@nxp.com>; Sascha Hauer
>>>>>>>> <s.hauer@pengutronix.de>; Shuah Khan
>>>>>>>> <skhan@linuxfoundation.org>; linux-
>>>>>>>> gpio@vger.kernel.org; linux-doc@vger.kernel.org; linux-
>>>>>>>> kernel@vger.kernel.org;
>>>>>>>> Pengutronix Kernel Team <kernel@pengutronix.de>; Fabio Estevam
>>>>>>>> <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
>>>>>>>> devicetree@vger.kernel.org; linux-remoteproc@vger.kernel.org;
>>>>>>>> imx@lists.linux.dev; linux-arm-kernel@lists.infradead.org; dl-
>>>>>>>> linux-imx <linux-
>>>>>>>> imx@nxp.com>; Bartosz Golaszewski <brgl@bgdev.pl>
>>>>>>>> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic
>>>>>>>> rpmsg GPIO driver
>>>>>>>> On Tue, Apr 28, 2026 at 03:24:59PM +0000, Shenwei Wang wrote:
>>>>>>>>>
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: Andrew Lunn <andrew@lunn.ch>
>>>>>>>>>> Sent: Monday, April 27, 2026 3:49 PM
>>>>>>>>>> To: Shenwei Wang <shenwei.wang@nxp.com>
>>>>>>>>>> Cc: Padhi, Beleswar <b-padhi@ti.com>; Linus Walleij
>>>>>>>>>> <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>;
>>>>>>>>>> Jonathan
>>>>>>>>>> Corbet <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof
>>>>>>>>>> Kozlowski <krzk+dt@kernel.org>; Conor Dooley
>>>>>>>>>> <conor+dt@kernel.org>;
>>>>>>>>>> Bjorn Andersson <andersson@kernel.org>; Mathieu Poirier
>>>>>>>>>> <mathieu.poirier@linaro.org>; Frank Li <frank.li@nxp.com>; Sascha
>>>>>>>>>> Hauer <s.hauer@pengutronix.de>; Shuah Khan
>>>>>>>>>> <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
>>>>>>>>>> doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix
>>>>>>>>>> Kernel Team <kernel@pengutronix.de>; Fabio Estevam
>>>>>>>>>> <festevam@gmail.com>; Peng Fan <peng.fan@nxp.com>;
>>>>>>>>>> devicetree@vger.kernel.org; linux- remoteproc@vger.kernel.org;
>>>>>>>>>> imx@lists.linux.dev; linux-arm- kernel@lists.infradead.org;
>>>>>>>>>> dl-linux-imx <linux-imx@nxp.com>; Bartosz Golaszewski
>>>>>>>>>> <brgl@bgdev.pl>
>>>>>>>>>> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg
>>>>>>>>>> GPIO driver
>>>>>>>>>>>> struct virtio_gpio_response {
>>>>>>>>>>>> __u8 status;
>>>>>>>>>>>> __u8 value;
>>>>>>>>>>>> };
>>>>>>>>>>> It is the same message format. Please see the message definition
>>>>>>>>>> (GET_DIRECTION) below:
>>>>>>>>>>
>>>>>>>>>>> + +-----+-----+-----+-----+-----+----+
>>>>>>>>>>> + |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
>>>>>>>>>>> + | 1 | 2 |port |line | err | dir|
>>>>>>>>>>> + +-----+-----+-----+-----+-----+----+
>>>>>>>>>> Sorry, but i don't see how two u8 vs six u8 are the same
>>>>>>>>>> message format.
>>>>>>>>>>
>>>>>>>>> Some changes to the message format are necessary.
>>>>>>>>>
>>>>>>>>> Virtio uses two communication channels (virtqueues): one for
>>>>>>>>> requests and
>>>>>>>> replies, and a second one for events.
>>>>>>>>> In contrast, rpmsg provides only a single communication
>>>>>>>>> channel, so a
>>>>>>>>> type field is required to distinguish between different kinds
>>>>>>>>> of messages.
>>>>>>>>>
>>>>>>>>> Since rpmsg replies and events share the same message format,
>>>>>>>>> an additional
>>>>>>>> line is introduced to handle both cases.
>>>>>>>>> Finally, rpmsg supports multiple GPIO controllers, so a port
>>>>>>>>> field is added to
>>>>>>>> uniquely identify the target controller.
>>>>>>>>
>>>>>>>> I have commented on this before - RPMSG is already providing
>>>>>>>> multiplexing
>>>>>>>> capability by way of endpoints. There is no need for a port
>>>>>>>> field. One endpoint,
>>>>>>>> one GPIO controller.
>>>>>>>>
>>>>>>> You still need a way to let the remote side know which port the
>>>>>>> endpoint maps to, either
>>>>>>> by embedding the port information in the message (the current
>>>>>>> way), or by sending it
>>>>>>> separately.
>>>>>>>
>>>>>> An endpoint is created with every namespace request. There should be
>>>>>> one namespace request for every GPIO controller, which yields a
>>>>>> unique
>>>>>> endpoint for each controller and eliminates the need for an extra
>>>>>> field to identify them.
>>>>>
>>>>>
>>>>> Right, but this can still be done by just having one namespace
>>>>> request.
>>>>> We can create new endpoints bound to an existing namespace/channel by
>>>>> invoking rpmsg_create_ept(). This is what I suggested here too:
>>>>> https://lore.kernel.org/all/29485742-6e49-482e-
>>>>> b73d-228295daaeec@ti.com/
>>>>>
>>>>
>>>> I will look at your suggestion (i.e link above) later this week or
>>>> next week.
>>>>
>>>>> My mental model looks like this for the complete picture:
>>>>>
>>>>> 1. namespace/channel#1 = rpmsg-io
>>>>> a. ept1 -> gpio-controller@1
>>>>> b. ept2 -> gpio-controller@2
>>>>>
If my understanding of what gpio-controller is right, than this won't
work. We need one rpmsg channel per gpio-controller, and in most cases
there will be only one GPIO-controller on the remote side. If there are
multiple or multiple instances of same controller, than we need separate
channel name for that controller just like we would have separate device
on the Linux.
>>>>
>>>> I've asked for one endpoint per GPIO controller since the very
>>>> beginning. I don't yet have a strong opinion on whether to use one
>>>> namespace request per GPIO controller or a single request that spins
>>>> off multiple endpoints. I'll have to look at your link and reflect on
>>>> that. Regardless of how we proceed on that front, multiplexing needs
>>>> to happen at the endpoint level rather than the packet level. This is
>>>> the only way this work can move forward.
>>>>
>>>
>>> I would be more in favor of Mathieu’s proposal: “An endpoint is
>>> created with every namespace request.”
>>>
>>> If the endpoint is created only on the Linux side, how do we match
>>> the Linux endpoint address with the local port field on the remote side?
>>
>>
>> Simply by sending a message to the remote containing the newly created
>> endpoint and the port idx. Note that is this done just one time, after
>> this
>> Linux need not have the port field in the message everytime its sending
>> a message.
>>
>>>
>>> With a multi-namespace approach, the namespace could be rpmsg-io-
>>> [addr], where [addr] corresponds to the GPIO controller address in
>>> the DT. This would:
>>
>>
>> You will face the same problem in this case also that you asked above:
>> "how do we match the Linux endpoint address with the local port field
>> on the remote side?"
>
> Sorry I probably introduced confusion here
> my sentence should be;
> With a multi-namespace approach, the namespace could be rpmsg-io-[port],
> where [port] corresponds to the GPIO controller port in the DT.
>
>
> For instance:
>
> rpmsg {
> rpmsg-io {
> #address-cells = <1>;
> #size-cells = <0>;
>
> gpio@25 {
> compatible = "rpmsg-gpio";
> reg = <25>;
> gpio-controller;
> #gpio-cells = <2>;
> #interrupt-cells = <2>;
> interrupt-controller;
> };
>
> gpio@32 {
> compatible = "rpmsg-gpio";
> reg = <32>;
> gpio-controller;
> #gpio-cells = <2>;
> #interrupt-cells = <2>;
> interrupt-controller;
> };
> };
> };
>
> rpmsg-io-25 would match with gpio@25
> rpmsg-io-32 would match with gpio@32
>
The problem with this approach is, we will endup creating way too many
RPMsg devices/channels. i.e. one channel per one GPIO. That limits how
many GPIOs can be handled by remote from memory perspective. At
somepoint we might just run-out of number ept & channels created by the
remote. As of now, open-amp library supports 128 epts I think.
>
>>
>> Because the endpoint that is created on a namespace request is also
>> dynamic in nature. How will the remote know which endpoint addr
>> Linux allocated for a namespace that it announced?
>>
>> As an example/PoC, I created a firmware example which announces
>> 2 name services to Linux, one is the standard "rpmsg_chrdev" and
>> the other is a TI specific name service "ti.ipc4.ping-pong". You can
>> see it created 2 different addresses (0x400 and 0x401) for each of
>> the name service request from the same firmware:
>>
>> root@j784s4-evm:~# dmesg | grep virtio0 | grep -i channel
>> [ 9.290275] virtio_rpmsg_bus virtio0: creating channel
>> ti.ipc4.ping-pong addr 0xd
>> [ 9.311230] virtio_rpmsg_bus virtio0: creating channel rpmsg_chrdev
>> addr 0xe
>> [ 9.496645] rpmsg_chrdev virtio0.rpmsg_chrdev.-1.14: DEBUG: Channel
>> formed from src = 0x400 to dst = 0xe
>> [ 9.707255] rpmsg_client_sample virtio0.ti.ipc4.ping-pong.-1.13:
>> new channel: 0x401 -> 0xd!
>>
>> So in this case, rpmsg-io-1 can have different ept addr than rpmsg-io-2
>> Back to same problem. Simple solution is to reply to remote with the
>> created ept addr and the index.
>
> That why I would like to suggest to use the name service field to
> identify the port/controller, instead of the endpoint address.
>>
>>>
>>> - match the RPMsg probe with the DT,
>>
>>
>> We can probe from all controllers with a single name service
>> announcement too.
>>
>>> - provide a simple mapping between the port and the endpoint on both
>>> sides,
>>
>>
>> We are trying to get rid of this mapping from Linux side to adapt
>> the gpio-virtio design.
>>
>>> - allow multiple endpoints on the remote side,
>>
>>
>> We can support this as well with single nameservice model.
>> There is no limitation. Remote has to send a message with
>> its newly created ept that's all.
>>
>>> - provide a simple discovery mechanism for remote capabilities.
>>
>>
>> A single announcement: "rpmsg-io" is also discovery mechanism.
>>
>> Feel free to let me know if you have concerns with any of the
>> suggestions!
>
> My only concern, whatever the solution, is that we find a smart
> solution to associate the correct endpoint with the correct GPIO
> port/controller defined in the DT.
>
> I may have misunderstood your solution. Could you please help me
> understand your proposal by explaining how you would handle three
> GPIO ports defined in the DT, considering that the endpoint
> addresses on the Linux side can be random?
> If I assume there is a unique endpoint on the remote side,
> I do not understand how you can match, on the firmware side,
> the Linux endpoint address to the GPIO port.
>
> Thanks and Regards,Arnaud
>
>>
>> Thanks,
>> Beleswar
>>
>>>
>>> Regards,
>>> Arnaud
>>>
>>>>> 2. namespace/channel#2 = rpmsg-i2c
>>>>> a. ept1 -> i2c@1
>>>>> b. ept2 -> i2c@2
>>>>> c. ept3 -> i2c@3
>>>>>
>>>>> etc...
>>>>>
Just want to clear-up few terms before I jump to the solution:
**RPMsg channel/device**:
- These are devices announced by the remote processor, and created by
linux. They are created at: /sys/bus/rpmsg/devices
- The channel format: <name>.<src ept>.<dst ept>
**RPMsg endpoint**:
- Endpoint is differnt than channel. Single channel can have multiple
endpoints, and represented in the linux with: /dev/rpmsg? devices.
To create endpoint device, we have rpmsg_create_ept API, which takes
channel information as input, which has src-ept, dst-ept.
Following is proposed solution:
1) Assign RPMsg channel/device per rpmsg-gpio controller (Not per GPIO
pin/port).
- In our case that would be, single rpmsg-io node. (That makes me
question if bindings are correct or not).
2) Assign GPIO number as src ept.
i.e. *rpmsg-io.<GPIO number>.<dst ept>*. Do not randomly assign src
endpoint.
Now, RPMSG channel by spec reserves first 1024 endpoints [1], so we can
add 1024 offset to the GPIO number:
so, when calling rpmsg_create_ept() API, we assing src_endpoint as:
(GPIO_NUMBER + RPMSG_RESERVED_ADDRESSES)
Now on the remote side, there is single channel and only single-endpoint
is needed that is mapped to the rpmsg-io channel callback.
That callback will receive all the payloads from the Linux, which will
have src-ept i.e. (RPMSG_RESERVED_ADDRESSES + GPIO_NUMBER).
It can retrieve GPIO_NUMBER easily, and convert to appropriate pin based
on platform specific logic.
This doesn't need PORT information at all. Also it makes sure that
remote is using only single-endpoint so not much memory is used.
*Example*:
If only rpmsg-gpio channel is created by the remote side, than following
is the representation of the devices when GPIO 25, 26, 27 is assigned to
the rpmsg-io controller:
Linux Remote
rpmsg-channel: rpmsg-gpio.0x400.0x400
/dev/rpmsg0 - GPIO25 ept (rpmsg-gpio.0x419.0x400)-|
|
/dev/rpmsg1 - GPIO26 ept (rpmsg-gpio.0x41a.0x400)-|-> rpmsg-gpio.*.0x400
|
/dev/rpmsg2 - GPIO27 ept (rpmsg-gpio.0x41b.0x400)-| 0x400 ept callback.
*On remote side*:
ept_0x400_callback(..., int src_ept, ...,)
{
int gpio_num = src_ept - RPMSG_RESERVED_ADDRESSES;
// platform specific logic to convert gpio num to proper pin,
// just like you would convert gpio num to pin on a linux gpio controller.
}
My question on the binding:
Why each GPIO is represented with the separate node? I think rpmsg-gpio
can be represented just any other GPIO controller? Please let me know if
I am missing something. So rpmsg channel/rpmsg device is not created per
GPIO, but per controller. GPIO number multiplexing should be done with
rpmsg src ept, that removes the need of having each GPIO as a separate node.
rpmsg_gpio: rpmsg-gpio@0 {
compatible = "rpmsg-gpio";
reg = <0>;
gpio-controller;
#gpio-cells = <2>;
#interrupt-cells = <2>;
interrupt-controller;
};
Then in DT, use like regular GPIO, but with the rpmsg-gpio controller:
rpmsg-gpios = <&rpmsg_gpio (GPIO NUM) (flags)>;
If the intent to create separate gpio nodes was only for the channel
creation, then it's not really needed.
[1]
https://github.com/torvalds/linux/blob/6d35786de28116ecf78797a62b84e6bf3c45aa5a/drivers/rpmsg/virtio_rpmsg_bus.c#L136
>>>>> This way device groups are isolated with each channel/namespace, and
>>>>> instances within each device groups are also respected with specific
>>>>> endpoints.
>>>>>
>>>>> Thanks,
>>>>> Beleswar
>>>>>
>>>>
>>>
>
>
^ permalink raw reply
* Re: [PATCH] crypto: af_alg - Document the deprecation of AF_ALG
From: Simo Sorce @ 2026-05-04 18:27 UTC (permalink / raw)
To: Jeff Barnes, Eric Biggers
Cc: Jon Kohler, linux-crypto@vger.kernel.org, Herbert Xu,
linux-doc@vger.kernel.org, linux-api@vger.kernel.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
Linus Torvalds
In-Reply-To: <F100C726-F841-461B-BE2F-C2018C122426@getmailspring.com>
On Mon, 2026-05-04 at 14:12 -0400, Jeff Barnes wrote:
>
> On May 4 2026, at 1:39 pm, Eric Biggers <ebiggers@kernel.org> wrote:
> >
> > That seems to be an implementation of FIPS 140-3's integrity self-check.
> > A few observations:
> >
> > - It could easily use userspace SHA-512 code instead. If including
> > libcrypto.so in the "FIPS cryptographic boundary" would cause
> > certification difficulties, then a sha512.c file could simply be added
> > to 'libkcapi-hmaccalc' which is already in it.
>
> Indeed expanding the crypto boundary to include libcrypto.so would cause
> certification difficulties, it would mean certifying all of libcrypto.so
> with the kernel. There *may* be a case for saying that it is outside the
> module boundary but only if:
>
> * The integrity mechanism is clearly external
> * The cryptographic module refuses to operate unless integrity is confirmed
> * The trust relationship is clearly documented
>
> I don't see how this could be justified cleanly without significant pushback.
>
> >
> > - It's compatible with all of the proposed hardening. It doesn't
> > require zero-copy performance. It runs as root, so it would be
> > compatible with a capability check. "hmac(sha512)" will need to be on
> > the algorithm allowlist anyway for iwd.
> >
> > - FIPS 140-3 might also allow it to be simplified to use a plain hash
> > instead of pointlessly using HMAC with a fixed key.
>
> FIPS 140‑3 (via ISO/IEC 19790) draws a hard distinction between:
> * Integrity checking (cryptographic protection)
> * Integrity measurement (detection only)
>
> A plain hash provides no protection against an attacker who can modify
> both the object and its reference hash.
The integrity mechanism is not build to detect adversarial tampering
(at least at level 1), that is not its purpose.
That said a HMAC is just a hash with a secret key in the mix, it does
not change the conditions wrt Hash if the key is known.
> > By the way, also on the topic of FIPS 140-3, some people do use AF_ALG
> > for ACVP (even though it's not all that great for that purpose, either).
> > But ACVP is a testing thing, not something that is needed on production
> > systems. ACVP can just be run as root on a testing build; there's no
> > need to enable support for it in the actual production build.
>
> Agreed it's not a good use case. Unless/until pkcs1 is supported, I
> don't see how you can use it for all of the test cases. Plus as
> evidenced by Ubuntu's new cert, it requires validating the library.
Of course it requires validating the library, validating the
cryptography implementation is what FIPS is for. libcrypto.so can never
be outside the boundary, unless you turn it off at runtime ...
The problems are rather that libcrypto is not instrumented to enforce
KAT tests are executed before it allows operation (crypto-API did this
via test manager) and does not provide an indicator (or blocks)
unapproved algorithms ...
Simo.
--
Simo Sorce
Distinguished Engineer
RHEL Crypto Team
Red Hat, Inc
^ permalink raw reply
* Re: [PATCH] crypto: af_alg - Document the deprecation of AF_ALG
From: Eric Biggers @ 2026-05-04 18:24 UTC (permalink / raw)
To: Jeff Barnes
Cc: Jon Kohler, linux-crypto@vger.kernel.org, Herbert Xu,
linux-doc@vger.kernel.org, linux-api@vger.kernel.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
Linus Torvalds
In-Reply-To: <F100C726-F841-461B-BE2F-C2018C122426@getmailspring.com>
On Mon, May 04, 2026 at 02:12:11PM -0400, Jeff Barnes wrote:
> A plain hash provides no protection against an attacker who can modify
> both the object and its reference hash.
Same with the HMAC, because in the FIPS integrity check the key isn't
secret. You can find the key used by the sha512hmac binary here:
https://github.com/smuellerDD/libkcapi/blob/master/apps/kcapi-hasher.c#L125
- Eric
^ permalink raw reply
* [PATCH v1] docs/ja_JP: translate more of submitting-patches.rst
From: Akiyoshi Kurita @ 2026-05-04 18:24 UTC (permalink / raw)
To: linux-doc; +Cc: linux-kernel, corbet, akiyks, Akiyoshi Kurita
Translate the "No MIME, no links, no compression, no attachments.
Just plain text" and "Respond to review comments" sections in
Documentation/translations/ja_JP/process/submitting-patches.rst.
Keep the wording close to the English text and wrap lines to match
the style used in the surrounding Japanese translation.
Signed-off-by: Akiyoshi Kurita <weibu@redadmin.org>
---
.../ja_JP/process/submitting-patches.rst | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/Documentation/translations/ja_JP/process/submitting-patches.rst b/Documentation/translations/ja_JP/process/submitting-patches.rst
index 928e38a8d34d..d7e04c09f951 100644
--- a/Documentation/translations/ja_JP/process/submitting-patches.rst
+++ b/Documentation/translations/ja_JP/process/submitting-patches.rst
@@ -292,3 +292,61 @@ MAINTAINERS ファイルに記載されている MAN-PAGES メンテナに
man-pages パッチ、少なくとも変更の通知を送って、情報が
マニュアルページに反映されるようにしてください。ユーザー空間 API の
変更は、linux-api@vger.kernel.org にも Cc してください。
+
+MIME、リンク、圧縮、添付ファイルは使わない。プレーンテキストだけ
+----------------------------------------------------------------------
+
+Linus や他のカーネル開発者は、あなたが投稿する変更を読み、
+コメントできる必要があります。カーネル開発者が標準的な
+メールツールを使ってあなたの変更を「引用」し、コードの特定の
+箇所についてコメントできることが重要です。
+
+このため、すべてのパッチはメール本文中に ``inline`` で投稿すべきです。
+これを行う最も簡単な方法は ``git send-email`` を使うことであり、
+強く推奨されます。``git send-email`` の対話型チュートリアルは
+https://git-send-email.io で利用できます。
+
+``git send-email`` を使わないことを選ぶ場合:
+
+.. warning::
+
+ パッチをコピー&ペーストする場合は、エディタの word-wrap によって
+ パッチが壊れないよう注意してください。
+
+圧縮の有無にかかわらず、パッチを MIME 添付ファイルとして添付しては
+いけません。多くの一般的なメールアプリケーションは、MIME 添付
+ファイルを常にプレーンテキストとして送信するとは限らず、あなたの
+コードにコメントできなくなります。MIME 添付ファイルは Linus が
+処理するのにも少し余分な時間がかかるため、MIME 添付された変更が
+受け入れられる可能性を下げます。
+
+例外: メーラがパッチを壊してしまう場合は、誰かから MIME を使って
+再送するよう求められることがあります。
+
+パッチを変更せずに送信するようメールクライアントを設定するための
+ヒントについては、Documentation/process/email-clients.rst を参照してください。
+
+
+レビューコメントに返答する
+--------------------------
+
+あなたのパッチには、ほぼ確実に、パッチを改善する方法について
+レビューアからコメントが付きます。それは、あなたのメールへの返信という
+形で届きます。それらのコメントには必ず返答してください。レビューアを
+無視することは、こちらも無視されるためのよい方法です。コメントに
+答えるには、単にそのメールへ返信すれば構いません。コード変更に
+つながらないレビューコメントや質問であっても、次のレビューアが状況を
+よりよく理解できるように、ほぼ確実にコメントまたは changelog エントリに
+反映すべきです。
+
+どのような変更を行うのかをレビューアに必ず伝え、時間を割いてくれた
+ことに感謝してください。コードレビューは疲れる、時間のかかる作業であり、
+レビューアが不機嫌になることもあります。そのような場合であっても、
+丁寧に返答し、指摘された問題に対応してください。次の版を送るときは、
+cover letter または個々のパッチに ``patch changelog`` を追加し、前回の
+投稿との差分を説明してください(:ref:`the_canonical_patch_format` を
+参照してください)。あなたのパッチにコメントした人には、パッチの Cc
+リストに追加して、新しい版を知らせてください。
+
+メールクライアントとメーリングリストでの作法についての推奨事項は、
+Documentation/process/email-clients.rst を参照してください。
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox