Re: [PATCH v11 20/21] KVM: arm64: PMU: Introduce FIXED_COUNTERS_ONLY
From: Oliver Upton
Date: Tue Sep 22 2026 - 03:14:26 EST
On Sun, Sep 20, 2026 at 08:16:01PM +0900, Akihiko Odaki wrote:
> Introduce the KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY attribute to
> create a "fixed-counters-only" PMU.
>
> Much like KVM_ARM_VCPU_PMU_V3_IRQ and other read-write attributes, this
> attribute provides a getter that facilitates kernel and userspace
> debugging/testing.
>
> Allow strict PMUv3 vCPUs to initialize the PMU after selecting
> fixed-counters-only mode, without selecting a VM-wide hardware PMU.
>
> Assisted-by: Codex:gpt-5.6-sol
> Signed-off-by: Akihiko Odaki <odaki@xxxxxxxxxxxxxxxxxxxxxx>
> ---
> Documentation/virt/kvm/api.rst | 8 +++++--
> Documentation/virt/kvm/devices/vcpu.rst | 42 ++++++++++++++++++++++++++-------
> arch/arm64/include/uapi/asm/kvm.h | 1 +
> arch/arm64/kvm/pmu-emul.c | 34 ++++++++++++++++++++++----
> tools/arch/arm64/include/uapi/asm/kvm.h | 1 +
> 5 files changed, 71 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index 1a90598901c5..a6aef405ef87 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -3531,9 +3531,13 @@ Possible features:
>
> * Userspace must explicitly select a PMU implementation before
> initializing the PMU or configuring a PMU event filter
> + (KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY also satisfies the
> + initialization requirement; event filtering remains unavailable
> + in that mode)
>
> - * If the PMU implements FEAT_PMUv3p4, PMMIR_EL1.SLOTS provides the
> - hardware value of the underlying implementation
> + * If a hardware PMU is selected and implements FEAT_PMUv3p4,
> + PMMIR_EL1.SLOTS provides its hardware value. In fixed-counters-only
> + mode, PMMIR_EL1 reads as zero
>
> * Writes to PMCR_EL0.N via KVM_SET_ONE_REG are ignored
>
> diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
> index a3f20a4ee4b1..41f6d708ff43 100644
> --- a/Documentation/virt/kvm/devices/vcpu.rst
> +++ b/Documentation/virt/kvm/devices/vcpu.rst
> @@ -53,9 +53,9 @@ Returns:
> ======= ======================================================
> -EEXIST Interrupt number already used
> -ENODEV PMUv3 not supported or GIC not initialized
> - -ENXIO PMUv3 not supported, missing VCPU feature, missing
> - hardware PMU, or interrupt number not set (non-GICv5
> - guests, only)
> + -ENXIO PMUv3 not supported, missing VCPU feature,
> + neither hardware PMU nor FIXED_COUNTERS_ONLY selected,
> + or interrupt number not set (non-GICv5 guests only)
> -EBUSY PMUv3 already initialized
> ======= ======================================================
>
> @@ -64,7 +64,8 @@ virtual GIC implementation, this must be done after initializing the in-kernel
> irqchip.
>
> When the KVM_ARM_VCPU_PMU_V3_STRICT vCPU feature is enabled this must be done
> -after selecting a hardware PMU.
> +after selecting a hardware PMU or enabling
> +KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY.
>
> 1.3 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FILTER
> -----------------------------------------
> @@ -78,7 +79,8 @@ after selecting a hardware PMU.
> -ENODEV PMUv3 not supported or GIC not initialized
> -ENXIO PMUv3 not properly configured or in-kernel irqchip not
> configured as required prior to calling this attribute
> - -EBUSY PMUv3 already initialized or a VCPU has already run
> + -EBUSY PMUv3 already initialized, a VCPU has already run or
> + FIXED_COUNTERS_ONLY has already been set
> -EINVAL Invalid filter range
> ======= ======================================================
>
> @@ -123,14 +125,14 @@ after selecting a hardware PMU.
>
> :Returns:
>
> - ======= ====================================================
> + ======= ===========================================================
> -EBUSY PMUv3 already initialized, a VCPU has already run or
> - an event filter has already been set
> + an event filter or FIXED_COUNTERS_ONLY has already been set
> -EFAULT Error accessing the PMU identifier
> -ENXIO PMU not found
> -ENODEV PMUv3 not supported or GIC not initialized
> -ENOMEM Could not allocate memory
> - ======= ====================================================
> + ======= ===========================================================
>
> Request that the VCPU uses the specified hardware PMU when creating guest events
> for the purpose of PMU emulation. The PMU identifier can be read from the "type"
> @@ -172,6 +174,30 @@ explicitly selected, or the number of counters is out of range for the
> selected PMU. Selecting a new PMU cancels the effect of setting this
> attribute.
>
> +1.6 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY
> +------------------------------------------------------
> +
> +:Parameters: no additional parameter in kvm_device_attr.addr
> +
> +:Returns:
> +
> + ======= ==================================================
> + -EBUSY PMUv3 already initialized, a VCPU has already run,
> + an event filter has already been set or
> + a hardware PMU has already been specified
> + -ENXIO Attempted to get before setting
> + -ENODEV Attempted to set while PMUv3 not supported
> + ======= ==================================================
> +
> +If set, KVM emulates PMUv3 without programmable event counters.
> +
> +With KVM_ARM_VCPU_PMU_V3_STRICT, enabling this attribute satisfies the
> +PMU selection requirement for KVM_ARM_VCPU_PMU_V3_INIT.
> +
> +When this attribute is enabled, the vCPU can run on any physical CPU
> +that has a PMU, regardless of the underlying implementation. This
> +attribute is VM-scoped.
> +
> 2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
> =================================
>
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 65ee09bd1463..7a6a0af14f5a 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -453,6 +453,7 @@ struct kvm_vgic_v5_ist {
> #define KVM_ARM_VCPU_PMU_V3_FILTER 2
> #define KVM_ARM_VCPU_PMU_V3_SET_PMU 3
> #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4
> +#define KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY 5
> #define KVM_ARM_VCPU_TIMER_CTRL 1
> #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0
> #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1
> diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
> index 9c1e2f3e41db..ebb648eabdc6 100644
> --- a/arch/arm64/kvm/pmu-emul.c
> +++ b/arch/arm64/kvm/pmu-emul.c
> @@ -1017,8 +1017,8 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
>
> static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
> {
> - /* Only possible when using KVM_ARM_VCPU_PMU_V3_STRICT */
> - if (!vcpu->kvm->arch.arm_pmu)
> + /* Strict PMUv3 requires a hardware PMU or fixed-counters-only mode. */
> + if (!vcpu->kvm->arch.arm_pmu && !kvm_pmu_fixed_counters_only(vcpu->kvm))
> return -ENXIO;
>
> if (irqchip_in_kernel(vcpu->kvm)) {
> @@ -1153,8 +1153,8 @@ int kvm_arm_set_default_pmu(struct kvm *kvm)
> * 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.
> + * to select a PMU type directly, or request fixed-counters-only
> + * emulation. But, be wary of the baggage being carried here.
> */
> struct arm_pmu *arm_pmu = kvm_pmu_probe_armpmu(raw_smp_processor_id());
>
> @@ -1179,11 +1179,13 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
> arm_pmu = entry->arm_pmu;
> if (arm_pmu->pmu.type == pmu_id) {
> if (kvm_vm_has_ran_once(kvm) ||
> + kvm_pmu_fixed_counters_only(kvm) ||
> (kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
> ret = -EBUSY;
> break;
> }
>
> + set_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, &kvm->arch.flags);
> kvm_arm_set_pmu(kvm, arm_pmu);
> cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
>
> @@ -1205,6 +1207,22 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
> return ret;
> }
>
> +static int kvm_arm_pmu_v3_set_pmu_fixed_counters_only(struct kvm_vcpu *vcpu)
> +{
> + struct kvm *kvm = vcpu->kvm;
> +
> + lockdep_assert_held(&kvm->arch.config_lock);
> +
> + if (kvm_vm_has_ran_once(kvm) || kvm->arch.pmu_filter ||
> + test_bit(KVM_ARCH_FLAG_PMU_V3_EXPLICIT, &kvm->arch.flags))
> + return -EBUSY;
> +
> + set_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY, &kvm->arch.flags);
> + kvm->arch.nr_pmu_counters = 0;
> +
> + return 0;
> +}
> +
I'd rather that FIXED_COUNTERS_ONLY depend on the KVM_ARM_VCPU_PMU_V3_STRICT
feature flag. The expectation of that flag is that userspace specifies a
backing PMU implementation; your FIXED_COUNTERS_ONLY could just be seen
as one such PMU implementation.
Thanks,
Oliver