On Mon, Jun 02, 2025 at 07:26:51PM +0000, Colton Lewis wrote:
static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) {
- u8 hpmn = vcpu->kvm->arch.arm_pmu->hpmn;
- preempt_disable();
/* * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK * to disable guest access to the profiling and trace buffers */
- vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN,
*host_data_ptr(nr_event_counters));
- vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
- vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, hpmn);
- vcpu->arch.mdcr_el2 |= (MDCR_EL2_HPMD |
MDCR_EL2_TPM |
This isn't safe, as there's no guarantee that kvm_arch::arm_pmu is pointing that the PMU for this CPU. KVM needs to derive HPMN from some per-CPU state, not anything tied to the VM/vCPU.
+/**
- kvm_pmu_partition() - Partition the PMU
- @pmu: Pointer to pmu being partitioned
- @host_counters: Number of host counters to reserve
- Partition the given PMU by taking a number of host counters to
- reserve and, if it is a valid reservation, recording the
- corresponding HPMN value in the hpmn field of the PMU and clearing
- the guest-reserved counters from the counter mask.
- Passing 0 for @host_counters has the effect of disabling partitioning.
- Return: 0 on success, -ERROR otherwise
- */
+int kvm_pmu_partition(struct arm_pmu *pmu, u8 host_counters) +{
- u8 nr_counters;
- u8 hpmn;
- if (!kvm_pmu_reservation_is_valid(host_counters))
return -EINVAL;
- nr_counters = *host_data_ptr(nr_event_counters);
- hpmn = kvm_pmu_hpmn(host_counters);
- if (hpmn < nr_counters) {
pmu->hpmn = hpmn;
/* Inform host driver of available counters */
bitmap_clear(pmu->cntr_mask, 0, hpmn);
bitmap_set(pmu->cntr_mask, hpmn, nr_counters);
clear_bit(ARMV8_PMU_CYCLE_IDX, pmu->cntr_mask);
if (pmuv3_has_icntr())
clear_bit(ARMV8_PMU_INSTR_IDX, pmu->cntr_mask);
kvm_debug("Partitioned PMU with HPMN %u", hpmn);
- } else {
pmu->hpmn = nr_counters;
bitmap_set(pmu->cntr_mask, 0, nr_counters);
set_bit(ARMV8_PMU_CYCLE_IDX, pmu->cntr_mask);
if (pmuv3_has_icntr())
set_bit(ARMV8_PMU_INSTR_IDX, pmu->cntr_mask);
kvm_debug("Unpartitioned PMU");
- }
- return 0;
+}
Hmm... Just in terms of code organization I'm not sure I like having KVM twiddling with *host* support for PMUv3. Feels like the ARM PMU driver should own partitioning and KVM just takes what it can get.
@@ -239,6 +245,13 @@ void kvm_host_pmu_init(struct arm_pmu *pmu) if (!pmuv3_implemented(kvm_arm_pmu_get_pmuver_limit())) return;
- if (reserved_host_counters) {
if (kvm_pmu_partition_supported())
WARN_ON(kvm_pmu_partition(pmu, reserved_host_counters));
else
kvm_err("PMU Partition is not supported");
- }
Hasn't the ARM PMU been registered with perf at this point? Surely the driver wouldn't be very pleased with us ripping counters out from under its feet.
Thanks, Oliver