Add KVM_ARM_PMU_PARTITION to enable the partitioned PMU for a given
vCPU. Add a corresponding KVM_CAP_ARM_PMU_PARTITION to check for this
ability. This capability is allowed on an initialized vCPU where PMUv3
and VHE are supported.
However, because the underlying ability relies on the driver being
passed some command line arguments to configure the hardware partition
at boot, enabling the partitioned PMU will not be allowed without the
underlying driver configuration even though the capability exists.
Signed-off-by: Colton Lewis <coltonlewis@google.com>
---
Documentation/virt/kvm/api.rst | 21 +++++++++++++++++++++
arch/arm64/include/asm/kvm_pmu.h | 10 +++++++---
arch/arm64/kvm/arm.c | 20 ++++++++++++++++++++
arch/arm64/kvm/pmu-direct.c | 17 +++++++++++++++++
include/uapi/linux/kvm.h | 4 ++++
5 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 4ef3d8482000..7e76f7c87598 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6478,6 +6478,27 @@ the capability to be present.
`flags` must currently be zero.
+4.144 KVM_ARM_PARTITION_PMU
+---------------------------
+
+:Capability: KVM_CAP_ARM_PARTITION_PMU
+:Architectures: arm64
+:Type: vcpu ioctl
+:Parameters: arg[0] is a boolean to enable the partitioned PMU
+
+This API controls the PMU implementation used for VMs. The capability
+is only available if the host PMUv3 driver was configured for
+partitioning via the module parameters `arm-pmuv3.partition_pmu=y` and
+`arm-pmuv3.reserved_guest_counters=[0-$NR_COUNTERS]`. When enabled,
+VMs are configured to have direct hardware access to the most
+frequently used registers for the counters configured by the
+aforementioned module parameters, bypassing the KVM traps in the
+standard emulated PMU implementation and reducing overhead of any
+guest software that uses PMU capabilities such as `perf`.
+
+If the host driver was configured for partitioning but the partitioned
+PMU is disabled through this interface, the VM will use the legacy PMU
+that shares the host partition.
.. _kvm_run:
diff --git a/arch/arm64/include/asm/kvm_pmu.h b/arch/arm64/include/asm/kvm_pmu.h
index 908e43416b50..c9d5fe325864 100644
--- a/arch/arm64/include/asm/kvm_pmu.h
+++ b/arch/arm64/include/asm/kvm_pmu.h
@@ -110,6 +110,8 @@ u8 kvm_pmu_hpmn(struct kvm_vcpu *vcpu);
void kvm_pmu_load(struct kvm_vcpu *vcpu);
void kvm_pmu_put(struct kvm_vcpu *vcpu);
+void kvm_vcpu_pmu_partition_enable(struct kvm_vcpu *vcpu, bool enable);
+
#if !defined(__KVM_NVHE_HYPERVISOR__)
bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu);
bool kvm_vcpu_pmu_use_fgt(struct kvm_vcpu *vcpu);
@@ -296,17 +298,17 @@ static inline bool kvm_pmu_counter_is_hyp(struct kvm_vcpu *vcpu, unsigned int id
static inline void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu) {}
-static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
+static inline bool kvm_pmu_is_partitioned(void *)
{
return false;
}
-static inline u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
+static inline u64 kvm_pmu_host_counter_mask(void *)
{
return ~0;
}
-static inline u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu)
+static inline u64 kvm_pmu_guest_counter_mask(void *)
{
return ~0;
}
@@ -315,6 +317,8 @@ static inline void kvm_pmu_host_counters_enable(void) {}
static inline void kvm_pmu_host_counters_disable(void) {}
static inline void kvm_pmu_handle_guest_irq(u64 govf) {}
+static inline void kvm_vcpu_pmu_partition_enable(struct kvm_vcpu *vcpu, bool enable) {}
+
#endif
#endif
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 7c007ee44ecb..94274bee4e65 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -21,6 +21,7 @@
#include <linux/irqbypass.h>
#include <linux/sched/stat.h>
#include <linux/psci.h>
+#include <linux/perf/arm_pmu.h>
#include <trace/events/kvm.h>
#define CREATE_TRACE_POINTS
@@ -38,6 +39,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_nested.h>
+#include <asm/kvm_pmu.h>
#include <asm/kvm_pkvm.h>
#include <asm/kvm_pmu.h>
#include <asm/kvm_ptrauth.h>
@@ -383,6 +385,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_ARM_PMU_V3:
r = kvm_supports_guest_pmuv3();
break;
+ case KVM_CAP_ARM_PARTITION_PMU:
+ r = kvm_pmu_partition_supported();
+ break;
case KVM_CAP_ARM_INJECT_SERROR_ESR:
r = cpus_have_final_cap(ARM64_HAS_RAS_EXTN);
break;
@@ -1810,6 +1815,21 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
return kvm_arm_vcpu_finalize(vcpu, what);
}
+ case KVM_ARM_PARTITION_PMU: {
+ bool enable;
+
+ if (unlikely(!kvm_vcpu_initialized(vcpu)))
+ return -ENOEXEC;
+
+ if (!kvm_pmu_is_partitioned(vcpu->kvm->arch.arm_pmu))
+ return -EPERM;
+
+ if (copy_from_user(&enable, argp, sizeof(enable)))
+ return -EFAULT;
+
+ kvm_vcpu_pmu_partition_enable(vcpu, enable);
+ return 0;
+ }
default:
r = -EINVAL;
}
diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 80a3eb89fca1..04e7b6a1d749 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -56,6 +56,23 @@ bool kvm_vcpu_pmu_is_partitioned(struct kvm_vcpu *vcpu)
!kvm_pmu_regs_host_owned(vcpu);
}
+/**
+ * kvm_vcpu_pmu_partition_enable() - Enable/disable partition flag
+ * @vcpu: Pointer to vcpu
+ * @enable: Whether to enable or disable
+ *
+ * If we want to enable the partition, the guest is free to grab
+ * hardware by accessing PMU registers. Otherwise, the host maintains
+ * control.
+ */
+void kvm_vcpu_pmu_partition_enable(struct kvm_vcpu *vcpu, bool enable)
+{
+ if (enable)
+ vcpu->arch.pmu.owner = VCPU_REGISTER_FREE;
+ else
+ vcpu->arch.pmu.owner = VCPU_REGISTER_HOST_OWNED;
+}
+
/**
* kvm_vcpu_pmu_use_fgt() - Determine if we can use FGT
* @vcpu: Pointer to struct kvm_vcpu
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index c74cf8f73337..2f8a8d4cfe3c 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -935,6 +935,7 @@ struct kvm_enable_cap {
#define KVM_CAP_ARM_EL2_E2H0 241
#define KVM_CAP_RISCV_MP_STATE_RESET 242
#define KVM_CAP_GMEM_SHARED_MEM 243
+#define KVM_CAP_ARM_PARTITION_PMU 244
struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1413,6 +1414,9 @@ struct kvm_enc_region {
#define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2)
#define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2)
+/* Available with KVM_CAP_ARM_PARTITION_PMU */
+#define KVM_ARM_PARTITION_PMU _IOWR(KVMIO, 0xce, bool)
+
#define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0)
#define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1)
--
2.50.0.727.gbf7dc18ff4-goog
Hi Colton, kernel test robot noticed the following build warnings: [auto build test WARNING on 79150772457f4d45e38b842d786240c36bb1f97f] url: https://github.com/intel-lab-lkp/linux/commits/Colton-Lewis/arm64-cpufeature-Add-cpucap-for-HPMN0/20250715-070741 base: 79150772457f4d45e38b842d786240c36bb1f97f patch link: https://lore.kernel.org/r/20250714225917.1396543-23-coltonlewis%40google.com patch subject: [PATCH v4 22/23] KVM: arm64: Add ioctl to partition the PMU when supported config: arm64-randconfig-001-20250715 (https://download.01.org/0day-ci/archive/20250716/202507160120.lx4XxDb5-lkp@intel.com/config) compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250716/202507160120.lx4XxDb5-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202507160120.lx4XxDb5-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from arch/arm64/kernel/asm-offsets.c:15: In file included from include/linux/kvm_host.h:45: In file included from arch/arm64/include/asm/kvm_host.h:31: >> arch/arm64/include/asm/kvm_pmu.h:301:49: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline bool kvm_pmu_is_partitioned(void *) ^ arch/arm64/include/asm/kvm_pmu.h:306:51: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline u64 kvm_pmu_host_counter_mask(void *) ^ arch/arm64/include/asm/kvm_pmu.h:311:52: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline u64 kvm_pmu_guest_counter_mask(void *) ^ 3 warnings generated. -- In file included from arch/arm64/kernel/asm-offsets.c:15: In file included from include/linux/kvm_host.h:45: In file included from arch/arm64/include/asm/kvm_host.h:31: >> arch/arm64/include/asm/kvm_pmu.h:301:49: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline bool kvm_pmu_is_partitioned(void *) ^ arch/arm64/include/asm/kvm_pmu.h:306:51: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline u64 kvm_pmu_host_counter_mask(void *) ^ arch/arm64/include/asm/kvm_pmu.h:311:52: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions] static inline u64 kvm_pmu_guest_counter_mask(void *) ^ 3 warnings generated. vim +301 arch/arm64/include/asm/kvm_pmu.h 300 > 301 static inline bool kvm_pmu_is_partitioned(void *) 302 { 303 return false; 304 } 305 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Hi Colton, kernel test robot noticed the following build errors: [auto build test ERROR on 79150772457f4d45e38b842d786240c36bb1f97f] url: https://github.com/intel-lab-lkp/linux/commits/Colton-Lewis/arm64-cpufeature-Add-cpucap-for-HPMN0/20250715-070741 base: 79150772457f4d45e38b842d786240c36bb1f97f patch link: https://lore.kernel.org/r/20250714225917.1396543-23-coltonlewis%40google.com patch subject: [PATCH v4 22/23] KVM: arm64: Add ioctl to partition the PMU when supported config: arm64-randconfig-003-20250715 (https://download.01.org/0day-ci/archive/20250716/202507160129.vrvWpdVu-lkp@intel.com/config) compiler: aarch64-linux-gcc (GCC) 8.5.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250716/202507160129.vrvWpdVu-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202507160129.vrvWpdVu-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from arch/arm64/include/asm/kvm_host.h:31, from include/linux/kvm_host.h:45, from arch/arm64/kernel/asm-offsets.c:15: arch/arm64/include/asm/kvm_pmu.h: In function 'kvm_pmu_is_partitioned': >> arch/arm64/include/asm/kvm_pmu.h:301:43: error: parameter name omitted static inline bool kvm_pmu_is_partitioned(void *) ^~~~~~ arch/arm64/include/asm/kvm_pmu.h: In function 'kvm_pmu_host_counter_mask': arch/arm64/include/asm/kvm_pmu.h:306:45: error: parameter name omitted static inline u64 kvm_pmu_host_counter_mask(void *) ^~~~~~ arch/arm64/include/asm/kvm_pmu.h: In function 'kvm_pmu_guest_counter_mask': arch/arm64/include/asm/kvm_pmu.h:311:46: error: parameter name omitted static inline u64 kvm_pmu_guest_counter_mask(void *) ^~~~~~ make[3]: *** [scripts/Makefile.build:182: arch/arm64/kernel/asm-offsets.s] Error 1 shuffle=21662191 make[3]: Target 'prepare' not remade because of errors. make[2]: *** [Makefile:1274: prepare0] Error 2 shuffle=21662191 make[2]: Target 'prepare' not remade because of errors. make[1]: *** [Makefile:248: __sub-make] Error 2 shuffle=21662191 make[1]: Target 'prepare' not remade because of errors. make: *** [Makefile:248: __sub-make] Error 2 shuffle=21662191 make: Target 'prepare' not remade because of errors. vim +301 arch/arm64/include/asm/kvm_pmu.h 300 > 301 static inline bool kvm_pmu_is_partitioned(void *) 302 { 303 return false; 304 } 305 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
kernel test robot <lkp@intel.com> writes: > Hi Colton, > kernel test robot noticed the following build errors: > [auto build test ERROR on 79150772457f4d45e38b842d786240c36bb1f97f] > url: > https://github.com/intel-lab-lkp/linux/commits/Colton-Lewis/arm64-cpufeature-Add-cpucap-for-HPMN0/20250715-070741 > base: 79150772457f4d45e38b842d786240c36bb1f97f > patch link: > https://lore.kernel.org/r/20250714225917.1396543-23-coltonlewis%40google.com > patch subject: [PATCH v4 22/23] KVM: arm64: Add ioctl to partition the > PMU when supported > config: arm64-randconfig-003-20250715 > (https://download.01.org/0day-ci/archive/20250716/202507160129.vrvWpdVu-lkp@intel.com/config) > compiler: aarch64-linux-gcc (GCC) 8.5.0 > reproduce (this is a W=1 build): > (https://download.01.org/0day-ci/archive/20250716/202507160129.vrvWpdVu-lkp@intel.com/reproduce) > If you fix the issue in a separate patch/commit (i.e. not just a new > version of > the same patch/commit), kindly add following tags > | Reported-by: kernel test robot <lkp@intel.com> > | Closes: > https://lore.kernel.org/oe-kbuild-all/202507160129.vrvWpdVu-lkp@intel.com/ > All errors (new ones prefixed by >>): > In file included from arch/arm64/include/asm/kvm_host.h:31, > from include/linux/kvm_host.h:45, > from arch/arm64/kernel/asm-offsets.c:15: > arch/arm64/include/asm/kvm_pmu.h: In function 'kvm_pmu_is_partitioned': >>> arch/arm64/include/asm/kvm_pmu.h:301:43: error: parameter name omitted > static inline bool kvm_pmu_is_partitioned(void *) > ^~~~~~ > arch/arm64/include/asm/kvm_pmu.h: In > function 'kvm_pmu_host_counter_mask': > arch/arm64/include/asm/kvm_pmu.h:306:45: error: parameter name omitted > static inline u64 kvm_pmu_host_counter_mask(void *) > ^~~~~~ > arch/arm64/include/asm/kvm_pmu.h: In > function 'kvm_pmu_guest_counter_mask': > arch/arm64/include/asm/kvm_pmu.h:311:46: error: parameter name omitted > static inline u64 kvm_pmu_guest_counter_mask(void *) > ^~~~~~ > make[3]: *** [scripts/Makefile.build:182: > arch/arm64/kernel/asm-offsets.s] Error 1 shuffle=21662191 Same spot/declarations that triggered a build error on v2 and fixed in v3 but moved to void * in v4 rather than defining an empty arm_pmu struct due to v3 feedback. Notably I remembered to build this branch before sending this time but these definitions do not trigger an error or warning on gcc-14 with this .config and W=1. So I'm not sure how I could have reasonably caught this myself before sending. Anyway, I'll fix. It's nothing to give the parameters a name. > make[3]: Target 'prepare' not remade because of errors. > make[2]: *** [Makefile:1274: prepare0] Error 2 shuffle=21662191 > make[2]: Target 'prepare' not remade because of errors. > make[1]: *** [Makefile:248: __sub-make] Error 2 shuffle=21662191 > make[1]: Target 'prepare' not remade because of errors. > make: *** [Makefile:248: __sub-make] Error 2 shuffle=21662191 > make: Target 'prepare' not remade because of errors. > vim +301 arch/arm64/include/asm/kvm_pmu.h > 300 > > 301 static inline bool kvm_pmu_is_partitioned(void *) > 302 { > 303 return false; > 304 } > 305 > -- > 0-DAY CI Kernel Test Service > https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.