On Mon, 20 Jul 2026 at 06:39, Akihiko Odaki <[email protected]> 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. > > Assisted-by: Codex:gpt-5.6-sol > Signed-off-by: Akihiko Odaki <[email protected]>
In terms of the UAPI policy, I have no strong opinion. In terms of the implementation, it looks correct to me. FILTER/SET_PMU and FIXED_COUNTERS_ONLY are mutually exclusive both ways, and the EXPLICIT flag keeps that from blocking the default-PMU path. Reviewed-by: Fuad Tabba <[email protected]> Cheers, /fuad > --- > Documentation/virt/kvm/devices/vcpu.rst | 30 ++++++++++++++++++++++++++---- > arch/arm64/include/uapi/asm/kvm.h | 1 + > arch/arm64/kvm/pmu-emul.c | 30 +++++++++++++++++++++++++++--- > tools/arch/arm64/include/uapi/asm/kvm.h | 1 + > 4 files changed, 55 insertions(+), 7 deletions(-) > > diff --git a/Documentation/virt/kvm/devices/vcpu.rst > b/Documentation/virt/kvm/devices/vcpu.rst > index 66e714f2fcfa..1d592e70bdea 100644 > --- a/Documentation/virt/kvm/devices/vcpu.rst > +++ b/Documentation/virt/kvm/devices/vcpu.rst > @@ -74,7 +74,8 @@ irqchip. > -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 > ======= ====================================================== > > @@ -116,14 +117,14 @@ using event 0x11 (CPU_CYCLES). > > :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" > @@ -165,6 +166,27 @@ 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. > + > +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 1c13bfa2d38a..39a1a1e412e6 100644 > --- a/arch/arm64/include/uapi/asm/kvm.h > +++ b/arch/arm64/include/uapi/asm/kvm.h > @@ -437,6 +437,7 @@ enum { > #define KVM_ARM_VCPU_PMU_V3_FILTER 2 > #define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 > #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 > +#define KVM_ARM_VCPU_PMU_V3_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 1ec09ef28467..ff5d5f66a9c0 100644 > --- a/arch/arm64/kvm/pmu-emul.c > +++ b/arch/arm64/kvm/pmu-emul.c > @@ -1118,8 +1118,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()); > > @@ -1144,11 +1144,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); > ret = 0; > @@ -1159,6 +1161,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; > +} > + > static int kvm_arm_pmu_v3_set_nr_counters(struct kvm_vcpu *vcpu, unsigned > int n) > { > struct kvm *kvm = vcpu->kvm; > @@ -1236,7 +1254,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, > struct kvm_device_attr *attr) > filter.action != KVM_PMU_EVENT_DENY)) > return -EINVAL; > > - if (kvm_vm_has_ran_once(kvm)) > + if (kvm_vm_has_ran_once(kvm) || > kvm_pmu_fixed_counters_only(kvm)) > return -EBUSY; > > if (!kvm->arch.pmu_filter) { > @@ -1281,6 +1299,8 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, > struct kvm_device_attr *attr) > > return kvm_arm_pmu_v3_set_nr_counters(vcpu, n); > } > + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: > + return kvm_arm_pmu_v3_set_pmu_fixed_counters_only(vcpu); > case KVM_ARM_VCPU_PMU_V3_INIT: > return kvm_arm_pmu_v3_init(vcpu); > } > @@ -1307,6 +1327,9 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, > struct kvm_device_attr *attr) > irq = vcpu->arch.pmu.irq_num; > return put_user(irq, uaddr); > } > + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: > + if (kvm_pmu_fixed_counters_only(vcpu->kvm)) > + return 0; > } > > return -ENXIO; > @@ -1320,6 +1343,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, > struct kvm_device_attr *attr) > case KVM_ARM_VCPU_PMU_V3_FILTER: > case KVM_ARM_VCPU_PMU_V3_SET_PMU: > case KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS: > + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY: > if (kvm_vcpu_has_pmu(vcpu)) > return 0; > } > diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h > b/tools/arch/arm64/include/uapi/asm/kvm.h > index 1c13bfa2d38a..39a1a1e412e6 100644 > --- a/tools/arch/arm64/include/uapi/asm/kvm.h > +++ b/tools/arch/arm64/include/uapi/asm/kvm.h > @@ -437,6 +437,7 @@ enum { > #define KVM_ARM_VCPU_PMU_V3_FILTER 2 > #define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 > #define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 > +#define KVM_ARM_VCPU_PMU_V3_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 > > -- > 2.55.0 > >

