On 2026/09/22 16:11, Oliver Upton wrote:
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.

Hi Oliver,

Agreed. That will make the kernel code simpler by removing the need to track explicit hardware PMU selection separately with KVM_ARCH_FLAG_PMU_V3_EXPLICIT, since STRICT does not select a default PMU. Hopefully, it will also make maintaining the code a bit easier by reducing possible configuration combinations and encourage userspace to use STRICT.

I'll make this change for v2.

Regards,
Akihiko Odaki

Reply via email to