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 <[email protected]> > --- > 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

