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