[PATCH v2] KVM: arm64: Allow to limit number of PMU counters
Auger Eric
eric.auger at redhat.com
Thu Sep 10 04:35:23 EDT 2020
Hi Alex,
On 9/10/20 9:21 AM, Auger Eric wrote:
> Hi Alex,
>
> On 9/8/20 10:57 PM, Alexander Graf wrote:
>> We currently pass through the number of PMU counters that we have available
>> in hardware to guests. So if my host supports 10 concurrently active PMU
>> counters, my guest will be able to spawn 10 counters as well.
>>
>> This is undesireable if we also want to use the PMU on the host for
>> monitoring. In that case, we want to split the PMU between guest and
>> host.
> but don't we have a trap and emulate approach as opposed to current SPE
> implementation?
Looking again at the switch code (__pmu_switch_to_guest), I see we just
enable/disable the event counters owned by either the host/guest. I
thought this was more involved. So now it is clear.
Thanks
Eric
>>
>> To help that case, let's add a PMU attr that allows us to limit the number
>> of PMU counters that we expose. With this patch in place, user space can
>> keep some counters free for host use.
>>
>> Signed-off-by: Alexander Graf <graf at amazon.com>
>>
>> ---
>>
>> Because this patch touches the same code paths as the vPMU filtering one
>> and the vPMU filtering generalized a few conditions in the attr path,
>> I've based it on top. Please let me know if you want it independent instead.
>>
>> v1 -> v2:
>>
>> - Add documentation
>> - Add read support
>> ---
>> Documentation/virt/kvm/devices/vcpu.rst | 25 +++++++++++++++++++++++++
>> arch/arm64/include/uapi/asm/kvm.h | 7 ++++---
>> arch/arm64/kvm/pmu-emul.c | 32 ++++++++++++++++++++++++++++++++
>> arch/arm64/kvm/sys_regs.c | 5 +++++
>> include/kvm/arm_pmu.h | 1 +
>> 5 files changed, 67 insertions(+), 3 deletions(-)
>>
>> diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
>> index 203b91e93151..1a1c8d8c8b1d 100644
>> --- a/Documentation/virt/kvm/devices/vcpu.rst
>> +++ b/Documentation/virt/kvm/devices/vcpu.rst
>> @@ -102,6 +102,31 @@ isn't strictly speaking an event. Filtering the cycle counter is possible
>> using event 0x11 (CPU_CYCLES).
>>
>>
>> +1.4 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_NUM_EVENTS
>> +---------------------------------------------
>> +
>> +:Parameters: in kvm_device_attr.addr the address for the limit of concurrent
>> + events is a pointer to an int
>> +
>> +:Returns:
>> +
>> + ======= ======================================================
>> + -ENODEV: PMUv3 not supported
>> + -EBUSY: PMUv3 already initialized
>> + -EINVAL: Too large number of events
> s/events/event counters
>
> I see that in perf code indeed num_events is used for that but I think
> for the end-user the event counter terminology is better as it fits the
> ARM spec.
>> + ======= ======================================================
>> +
>> +Reconfigure the limit of concurrent PMU events that the guest can monitor.
> here also
>> +This number is directly exposed as part of the PMCR_EL0 register.
> Maybe quote the "N" field
>> +
>> +On vcpu creation, this attribute is set to the hardware limit of the current
>> +platform. If you need to determine the hardware limit, you can read this
>> +attribute before setting it.
>> +
>> +Restrictions: The default value for this property is the number of hardware
>> +supported events. Only values that are smaller than the hardware limit can
> event counters
>> +be set.
>> +
>> 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 7b1511d6ce44..db025c0b5a40 100644
>> --- a/arch/arm64/include/uapi/asm/kvm.h
>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>> @@ -342,9 +342,10 @@ struct kvm_vcpu_events {
>>
>> /* Device Control API on vcpu fd */
>> #define KVM_ARM_VCPU_PMU_V3_CTRL 0
>> -#define KVM_ARM_VCPU_PMU_V3_IRQ 0
>> -#define KVM_ARM_VCPU_PMU_V3_INIT 1
>> -#define KVM_ARM_VCPU_PMU_V3_FILTER 2
>> +#define KVM_ARM_VCPU_PMU_V3_IRQ 0
>> +#define KVM_ARM_VCPU_PMU_V3_INIT 1
>> +#define KVM_ARM_VCPU_PMU_V3_FILTER 2
>> +#define KVM_ARM_VCPU_PMU_V3_NUM_EVENTS 3
>> #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 0458860bade2..c7915b95fec0 100644
>> --- a/arch/arm64/kvm/pmu-emul.c
>> +++ b/arch/arm64/kvm/pmu-emul.c
>> @@ -253,6 +253,8 @@ void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu)
>>
>> for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++)
>> pmu->pmc[i].idx = i;
>> +
>> + pmu->num_events = perf_num_counters() - 1;
>> }
>>
>> /**
>> @@ -978,6 +980,25 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
>>
>> return 0;
>> }
>> + case KVM_ARM_VCPU_PMU_V3_NUM_EVENTS: {
>> + u64 mask = ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT;
>> + int __user *uaddr = (int __user *)(long)attr->addr;
>> + u32 num_events;
>> +
>> + if (get_user(num_events, uaddr))
>> + return -EFAULT;
>> +
>> + if (num_events >= perf_num_counters())
>> + return -EINVAL;
>> +
>> + vcpu->arch.pmu.num_events = num_events;
>> +
>> + num_events <<= ARMV8_PMU_PMCR_N_SHIFT;
>> + __vcpu_sys_reg(vcpu, SYS_PMCR_EL0) &= ~mask;
>> + __vcpu_sys_reg(vcpu, SYS_PMCR_EL0) |= num_events;
>> +
>> + return 0;
>> + }
>> case KVM_ARM_VCPU_PMU_V3_INIT:
>> return kvm_arm_pmu_v3_init(vcpu);
>> }
>> @@ -1004,6 +1025,16 @@ 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_NUM_EVENTS: {
>> + int __user *uaddr = (int __user *)(long)attr->addr;
>> + u32 num_events;
>> +
>> + if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
>> + return -ENODEV;
>> +
>> + num_events = vcpu->arch.pmu.num_events;
>> + return put_user(num_events, uaddr);
>> + }
>> }
>>
>> return -ENXIO;
>> @@ -1015,6 +1046,7 @@ int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
>> case KVM_ARM_VCPU_PMU_V3_IRQ:
>> case KVM_ARM_VCPU_PMU_V3_INIT:
>> case KVM_ARM_VCPU_PMU_V3_FILTER:
>> + case KVM_ARM_VCPU_PMU_V3_NUM_EVENTS:
>> if (kvm_arm_support_pmu_v3() &&
>> test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
>> return 0;
>> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
>> index 20ab2a7d37ca..d51e39600bbd 100644
>> --- a/arch/arm64/kvm/sys_regs.c
>> +++ b/arch/arm64/kvm/sys_regs.c
>> @@ -672,6 +672,11 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
>> | (ARMV8_PMU_PMCR_MASK & 0xdecafbad)) & (~ARMV8_PMU_PMCR_E);
>> if (!system_supports_32bit_el0())
>> val |= ARMV8_PMU_PMCR_LC;
>> +
>> + /* Override number of event selectors */
>> + val &= ~(ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
>> + val |= (u32)vcpu->arch.pmu.num_events << ARMV8_PMU_PMCR_N_SHIFT;
>> +
>> __vcpu_sys_reg(vcpu, r->reg) = val;
>> }
>>
>> diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
>> index 98cbfe885a53..ea3fc96a37d9 100644
>> --- a/include/kvm/arm_pmu.h
>> +++ b/include/kvm/arm_pmu.h
>> @@ -27,6 +27,7 @@ struct kvm_pmu {
>> bool ready;
>> bool created;
>> bool irq_level;
>> + u8 num_events;
>> };
>>
>> #define kvm_arm_pmu_v3_ready(v) ((v)->arch.pmu.ready)
>>
>
> Thanks
>
> Eric
>
More information about the linux-arm-kernel
mailing list