]> www.infradead.org Git - users/hch/misc.git/commitdiff
KVM: arm64: Remap PMUv3 events onto hardware
authorOliver Upton <oliver.upton@linux.dev>
Wed, 5 Mar 2025 20:26:38 +0000 (12:26 -0800)
committerOliver Upton <oliver.upton@linux.dev>
Tue, 11 Mar 2025 19:54:29 +0000 (12:54 -0700)
Map PMUv3 event IDs onto hardware, if the driver exposes such a helper.
This is expected to be quite rare, and only useful for non-PMUv3 hardware.

Tested-by: Janne Grunau <j@jannau.net>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250305202641.428114-12-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/pmu-emul.c
include/linux/perf/arm_pmu.h

index 38d9490c17fd9aa48d2df9f02b91d20bd1482790..5f4e9be8aa8bfe723d33e02d7ef5253feaf94277 100644 (file)
@@ -677,6 +677,20 @@ static bool kvm_pmc_counts_at_el2(struct kvm_pmc *pmc)
        return kvm_pmc_read_evtreg(pmc) & ARMV8_PMU_INCLUDE_EL2;
 }
 
+static int kvm_map_pmu_event(struct kvm *kvm, unsigned int eventsel)
+{
+       struct arm_pmu *pmu = kvm->arch.arm_pmu;
+
+       /*
+        * The CPU PMU likely isn't PMUv3; let the driver provide a mapping
+        * for the guest's PMUv3 event ID.
+        */
+       if (unlikely(pmu->map_pmuv3_event))
+               return pmu->map_pmuv3_event(eventsel);
+
+       return eventsel;
+}
+
 /**
  * kvm_pmu_create_perf_event - create a perf event for a counter
  * @pmc: Counter context
@@ -687,7 +701,8 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
        struct arm_pmu *arm_pmu = vcpu->kvm->arch.arm_pmu;
        struct perf_event *event;
        struct perf_event_attr attr;
-       u64 eventsel, evtreg;
+       int eventsel;
+       u64 evtreg;
 
        evtreg = kvm_pmc_read_evtreg(pmc);
 
@@ -713,6 +728,14 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
            !test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
                return;
 
+       /*
+        * Don't create an event if we're running on hardware that requires
+        * PMUv3 event translation and we couldn't find a valid mapping.
+        */
+       eventsel = kvm_map_pmu_event(vcpu->kvm, eventsel);
+       if (eventsel < 0)
+               return;
+
        memset(&attr, 0, sizeof(struct perf_event_attr));
        attr.type = arm_pmu->pmu.type;
        attr.size = sizeof(attr);
index 4b5b83677e3f28b578c1d63a5f89b5f248e06876..7ce6dea5bfa9f0864fe6039b39ddf8a382b14459 100644 (file)
@@ -100,6 +100,10 @@ struct arm_pmu {
        void            (*stop)(struct arm_pmu *);
        void            (*reset)(void *);
        int             (*map_event)(struct perf_event *event);
+       /*
+        * Called by KVM to map the PMUv3 event space onto non-PMUv3 hardware.
+        */
+       int             (*map_pmuv3_event)(unsigned int eventsel);
        DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS);
        bool            secure_access; /* 32-bit ARM only */
 #define ARMV8_PMUV3_MAX_COMMON_EVENTS          0x40