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
>
>

Reply via email to