[PATCH v6 06/19] perf: arm_pmuv3: Keep out of guest counter partition

Colton Lewis coltonlewis at google.com
Thu Mar 12 15:39:04 PDT 2026


James Clark <james.clark at linaro.org> writes:

> On 09/02/2026 10:14 pm, Colton Lewis wrote:
>> If the PMU is partitioned, keep the driver out of the guest counter
>> partition and only use the host counter partition.

>> Define some functions that determine whether the PMU is partitioned
>> and construct mutually exclusive bitmaps for testing which partition a
>> particular counter is in. Note that despite their separate position in
>> the bitmap, the cycle and instruction counters are always in the guest
>> partition.

>> Signed-off-by: Colton Lewis <coltonlewis at google.com>
>> ---
>>    arch/arm/include/asm/arm_pmuv3.h | 18 +++++++
>>    arch/arm64/kvm/pmu-direct.c      | 86 ++++++++++++++++++++++++++++++++
>>    drivers/perf/arm_pmuv3.c         | 40 +++++++++++++--
>>    include/kvm/arm_pmu.h            | 24 +++++++++
>>    4 files changed, 164 insertions(+), 4 deletions(-)

>> diff --git a/arch/arm/include/asm/arm_pmuv3.h  
>> b/arch/arm/include/asm/arm_pmuv3.h
>> index 154503f054886..bed4dfa755681 100644
>> --- a/arch/arm/include/asm/arm_pmuv3.h
>> +++ b/arch/arm/include/asm/arm_pmuv3.h
>> @@ -231,6 +231,24 @@ static inline bool kvm_set_pmuserenr(u64 val)
>>    }

>>    static inline void kvm_vcpu_pmu_resync_el0(void) {}
>> +static inline void kvm_pmu_host_counters_enable(void) {}
>> +static inline void kvm_pmu_host_counters_disable(void) {}
>> +
>> +static inline bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
>> +{
>> +	return false;
>> +}
>> +
>> +static inline u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
>> +{
>> +	return ~0;
>> +}
>> +
>> +static inline u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu)
>> +{
>> +	return ~0;
>> +}
>> +

>>    /* PMU Version in DFR Register */
>>    #define ARMV8_PMU_DFR_VER_NI        0
>> diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
>> index 74e40e4915416..05ac38ec3ea20 100644
>> --- a/arch/arm64/kvm/pmu-direct.c
>> +++ b/arch/arm64/kvm/pmu-direct.c
>> @@ -5,6 +5,8 @@
>>     */

>>    #include <linux/kvm_host.h>
>> +#include <linux/perf/arm_pmu.h>
>> +#include <linux/perf/arm_pmuv3.h>

>>    #include <asm/arm_pmuv3.h>

>> @@ -20,3 +22,87 @@ bool has_host_pmu_partition_support(void)
>>    	return has_vhe() &&
>>    		system_supports_pmuv3();
>>    }
>> +
>> +/**
>> + * kvm_pmu_is_partitioned() - Determine if given PMU is partitioned
>> + * @pmu: Pointer to arm_pmu struct
>> + *
>> + * Determine if given PMU is partitioned by looking at hpmn field. The
>> + * PMU is partitioned if this field is less than the number of
>> + * counters in the system.
>> + *
>> + * Return: True if the PMU is partitioned, false otherwise
>> + */
>> +bool kvm_pmu_is_partitioned(struct arm_pmu *pmu)
>> +{
>> +	if (!pmu)
>> +		return false;
>> +
>> +	return pmu->max_guest_counters >= 0 &&
>> +		pmu->max_guest_counters <= *host_data_ptr(nr_event_counters);
>> +}
>> +
>> +/**
>> + * kvm_pmu_host_counter_mask() - Compute bitmask of host-reserved  
>> counters
>> + * @pmu: Pointer to arm_pmu struct
>> + *
>> + * Compute the bitmask that selects the host-reserved counters in the
>> + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
>> + * in HPMN..N
>> + *
>> + * Return: Bitmask
>> + */
>> +u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu)
>> +{
>> +	u8 nr_counters = *host_data_ptr(nr_event_counters);
>> +
>> +	if (!kvm_pmu_is_partitioned(pmu))
>> +		return ARMV8_PMU_CNT_MASK_ALL;
>> +
>> +	return GENMASK(nr_counters - 1, pmu->max_guest_counters);
>> +}
>> +
>> +/**
>> + * kvm_pmu_guest_counter_mask() - Compute bitmask of guest-reserved  
>> counters
>> + * @pmu: Pointer to arm_pmu struct
>> + *
>> + * Compute the bitmask that selects the guest-reserved counters in the
>> + * {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers. These are the counters
>> + * in 0..HPMN and the cycle and instruction counters.
>> + *
>> + * Return: Bitmask
>> + */
>> +u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu)
>> +{
>> +	return ARMV8_PMU_CNT_MASK_C & GENMASK(pmu->max_guest_counters - 1, 0);

> This should be an | instead of an & otherwise it's always zero. None of
> the passed through counters count anything with it like this, although
> the cycle counter always worked even with this issue.

> I'm not sure if the selftests that you added catch this? I didn't try
> running them but seems like checking for non zero counter values is a
> very easy thing to test.

I caught this myself and called it out here:

https://lore.kernel.org/kvmarm/gsntseaoogk7.fsf@coltonlewis-kvm.c.googlers.com/

The selftest didn't catch this, so you're right it's a good idea to check.



More information about the linux-arm-kernel mailing list