]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/msm: Add basic perfcntr infrastructure
authorRob Clark <robin.clark@oss.qualcomm.com>
Tue, 26 May 2026 14:50:44 +0000 (07:50 -0700)
committerRob Clark <robin.clark@oss.qualcomm.com>
Fri, 29 May 2026 14:07:29 +0000 (07:07 -0700)
Add the basic infrastructure for tracking assigned perfcntrs.

Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
Reviewed-by: Anna Maniscalco <anna.maniscalco2000@gmail.com>
Reviewed-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
Patchwork: https://patchwork.freedesktop.org/patch/728212/
Message-ID: <20260526145137.160554-11-robin.clark@oss.qualcomm.com>

drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/adreno/adreno_device.c
drivers/gpu/drm/msm/adreno/adreno_gpu.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/msm/msm_gpu.h
drivers/gpu/drm/msm/msm_perfcntr.c [new file with mode: 0644]
drivers/gpu/drm/msm/msm_perfcntr.h

index 363c984a62e68740038917dc5ea685b8af6e9697..d0c3a4c6703be29fe3101fdfc4319310e4825f87 100644 (file)
@@ -122,6 +122,7 @@ msm-y += \
        msm_gpu_devfreq.o \
        msm_io_utils.o \
        msm_iommu.o \
+       msm_perfcntr.o \
        msm_rd.o \
        msm_ringbuffer.o \
        msm_submitqueue.o \
index fc38331ce6408c0e2d6a829503c5495ab9209a0c..7f20320ef66af3a17e69ca327c6f8b2a873d6741 100644 (file)
@@ -307,8 +307,10 @@ MODULE_DEVICE_TABLE(of, dt_match);
 static int adreno_runtime_resume(struct device *dev)
 {
        struct msm_gpu *gpu = dev_to_gpu(dev);
-
-       return gpu->funcs->pm_resume(gpu);
+       int ret = gpu->funcs->pm_resume(gpu);
+       if (!ret)
+               ret = msm_perfcntr_resume(gpu);
+       return ret;
 }
 
 static int adreno_runtime_suspend(struct device *dev)
@@ -322,6 +324,8 @@ static int adreno_runtime_suspend(struct device *dev)
         */
        WARN_ON_ONCE(gpu->active_submits);
 
+       msm_perfcntr_suspend(gpu);
+
        return gpu->funcs->pm_suspend(gpu);
 }
 
index 69918975f7112d8e8214fd99c7c45075537a7517..c62c45bb0ddbba7d396f2f32afc5ff5cce2ffe75 100644 (file)
@@ -708,11 +708,10 @@ void adreno_recover(struct msm_gpu *gpu)
        struct drm_device *dev = gpu->dev;
        int ret;
 
-       // XXX pm-runtime??  we *need* the device to be off after this
-       // so maybe continuing to call ->pm_suspend/resume() is better?
-
+       msm_perfcntr_suspend(gpu);
        gpu->funcs->pm_suspend(gpu);
        gpu->funcs->pm_resume(gpu);
+       msm_perfcntr_resume(gpu);
 
        ret = msm_gpu_hw_init(gpu);
        if (ret) {
index e53e4f220bedb1edfc5c8a297cc2751806c4fdaa..f00b2e7aeb91234b08c503033756590f0f573d32 100644 (file)
@@ -235,6 +235,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 int msm_ioctl_vm_bind(struct drm_device *dev, void *data,
                      struct drm_file *file);
 
+int msm_perfcntr_resume(struct msm_gpu *gpu);
+void msm_perfcntr_suspend(struct msm_gpu *gpu);
+
+struct msm_perfcntr_state * msm_perfcntr_init(struct msm_gpu *gpu);
+void msm_perfcntr_cleanup(struct msm_gpu *gpu);
+
 #ifdef CONFIG_DEBUG_FS
 unsigned long msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_to_scan);
 #endif
index 08940bae3b6e27316171de3427a26e81689bb8e5..18ed00e5f143b9fdee0b612a166ad75872a8017c 100644 (file)
@@ -1010,6 +1010,17 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
 
        refcount_set(&gpu->sysprof_active, 1);
 
+       mutex_init(&gpu->perfcntr_lock);
+
+       if (gpu->num_perfcntr_groups > 0) {
+               gpu->perfcntrs = msm_perfcntr_init(gpu);
+               if (IS_ERR(gpu->perfcntrs)) {
+                       ret = PTR_ERR(gpu->perfcntrs);
+                       gpu->perfcntrs = NULL;
+                       goto fail;
+               }
+       }
+
        return 0;
 
 fail:
@@ -1048,6 +1059,7 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
        }
 
        msm_devfreq_cleanup(gpu);
+       msm_perfcntr_cleanup(gpu);
 
        platform_set_drvdata(gpu->pdev, NULL);
 }
index 677cd7d509d7279b255fc6cd767014b18b6ac527..ac124d5620372a81fe17ad1bdec3bec10c8ab520 100644 (file)
@@ -25,6 +25,7 @@ struct msm_gem_vm_log_entry;
 struct msm_gpu_state;
 struct msm_context;
 struct msm_perfcntr_group;
+struct msm_perfcntr_stream;
 
 struct msm_gpu_config {
        const char *ioname;
@@ -93,6 +94,13 @@ struct msm_gpu_funcs {
         */
        bool (*progress)(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
        void (*sysprof_setup)(struct msm_gpu *gpu);
+
+       /* Configure perfcntr SELect regs: */
+       void (*perfcntr_configure)(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
+                                  const struct msm_perfcntr_stream *stream);
+
+       /* Flush perfcntrs before reading (optional): */
+       void (*perfcntr_flush)(struct msm_gpu *gpu);
 };
 
 /* Additional state for iommu faults: */
@@ -266,6 +274,11 @@ struct msm_gpu {
 
        const struct msm_perfcntr_group *perfcntr_groups;
        unsigned num_perfcntr_groups;
+
+       struct msm_perfcntr_state *perfcntrs;
+
+       /** @perfcntr_lock: protects perfcntr related state */
+       struct mutex perfcntr_lock;
 };
 
 static inline struct msm_gpu *dev_to_gpu(struct device *dev)
@@ -311,10 +324,52 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu)
        return false;
 }
 
+/**
+ * struct msm_perfcntr_group_state - Tracking for the currently allocated counter state
+ */
+struct msm_perfcntr_group_state {
+       /**
+        * @allocated_counters:
+        *
+        * allocated counters for global counter collection.  The
+        * corresponding counters are allocated from highest to
+        * lowest, to minimize chance of conflict with old userspace
+        * allocating from lowest to highest.
+        */
+       unsigned allocated_counters;
+
+       /**
+        * @countables:
+        *
+        * The corresponding SELect reg values for the allocated counters
+        */
+       uint32_t countables[];
+};
+
+/**
+ * struct msm_perfcntr_state - overall global perfcntr state
+ */
+struct msm_perfcntr_state {
+       /** @stream: current global counter stream if active */
+       struct msm_perfcntr_stream *stream;
+
+       /**
+        * @groups: Global perfcntr stream group state.
+        *
+        * Conceptually this is part of msm_perfcntr_stream state, but is
+        * statically pre-allocated when the gpu is initialized to simplify
+        * error path cleanup in PERFCNTR_CONFIG ioctl.  (__free(kfree)
+        * doesn't really help with variable length arrays of allocated
+        * pointers.)
+        */
+       struct msm_perfcntr_group_state *groups[];
+};
+
 static inline bool
 msm_gpu_sysprof_no_perfcntr_zap(struct msm_gpu *gpu)
 {
-       return refcount_read(&gpu->sysprof_active) > 1;
+       return (refcount_read(&gpu->sysprof_active) > 1) ||
+               (gpu->perfcntrs && READ_ONCE(gpu->perfcntrs->stream));
 }
 
 static inline bool
diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c
new file mode 100644 (file)
index 0000000..aeea60c
--- /dev/null
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include "msm_drv.h"
+#include "msm_gpu.h"
+#include "msm_perfcntr.h"
+
+static int
+msm_perfcntr_resume_locked(struct msm_perfcntr_stream *stream)
+{
+       return 0;
+}
+
+int
+msm_perfcntr_resume(struct msm_gpu *gpu)
+{
+       if (!gpu->perfcntrs)
+               return 0;
+       guard(mutex)(&gpu->perfcntr_lock);
+       return msm_perfcntr_resume_locked(gpu->perfcntrs->stream);
+}
+
+static void
+msm_perfcntr_suspend_locked(struct msm_perfcntr_stream *stream)
+{
+}
+
+void
+msm_perfcntr_suspend(struct msm_gpu *gpu)
+{
+       if (!gpu->perfcntrs)
+               return;
+       guard(mutex)(&gpu->perfcntr_lock);
+       msm_perfcntr_suspend_locked(gpu->perfcntrs->stream);
+}
+
+/**
+ * msm_perfcntr_group_idx - map idx of perfcntr group to group_idx
+ * @stream: The global perfcntr stream
+ * @n: The requested group_idx
+ *
+ * The PERFCNTR_CONFIG ioctl requested N counters/countables per perfcntr
+ * group, but the order of groups is not required to match the order they
+ * are defined in the perfcntr tables (which is not stable/UABI, only the
+ * group names are UABI).
+ *
+ * But the order samples are returned in the stream should match the
+ * order they are requested in the PERFCNTR_CONFIG ioctl.  This helper
+ * handles the order remapping.
+ *
+ * Returns an index into gpu->perfcntr_groups[] and perfcntrs->groups[].
+ */
+uint32_t
+msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n)
+{
+       WARN_ON_ONCE(n >= stream->nr_groups);
+       return stream->group_idx[n];
+}
+
+/**
+ * msm_perfcntr_counter_base - get idx of the first counter in group
+ * @stream: The global perfcntr stream
+ * @group_idx: the index of the counter group
+ *
+ * For global counter collection, counters are allocated from the end
+ * (last counter) while UMD allocates them from the start (0..N-1).
+ * Since UMD always allocated them from the start this also minimizes
+ * the chance of conflict when using old UMD which predates
+ * PERFCNTR_CONFIG ioctl.
+ *
+ * Returns the index of first counter to use.  An index into
+ * msm_perfcntr_group::counters[].
+ */
+uint32_t
+msm_perfcntr_counter_base(const struct msm_perfcntr_stream *stream, uint32_t group_idx)
+{
+       struct msm_gpu *gpu = stream->gpu;
+       struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs;
+       unsigned num_counters = gpu->perfcntr_groups[group_idx].num_counters;
+       unsigned allocated_counters = perfcntrs->groups[group_idx]->allocated_counters;
+
+       return num_counters - allocated_counters;
+}
+
+static void
+__msm_perfcntr_cleanup(struct msm_gpu *gpu, struct msm_perfcntr_state *perfcntrs)
+{
+       struct device *dev = &gpu->pdev->dev;
+
+       for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++)
+               devm_kfree(dev, perfcntrs->groups[i]);
+
+       devm_kfree(dev, perfcntrs);
+}
+
+void
+msm_perfcntr_cleanup(struct msm_gpu *gpu)
+{
+       if (!gpu->perfcntrs)
+               return;
+
+       __msm_perfcntr_cleanup(gpu, gpu->perfcntrs);
+       gpu->perfcntrs = NULL;
+}
+
+struct msm_perfcntr_state *
+msm_perfcntr_init(struct msm_gpu *gpu)
+{
+       struct msm_perfcntr_state *perfcntrs;
+       struct device *dev = &gpu->pdev->dev;
+       size_t sz;
+
+       sz = struct_size(perfcntrs, groups, gpu->num_perfcntr_groups);
+       perfcntrs = devm_kzalloc(dev, sz, GFP_KERNEL);
+       if (!perfcntrs)
+               return ERR_PTR(-ENOMEM);
+
+       for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) {
+               const struct msm_perfcntr_group *group =
+                       &gpu->perfcntr_groups[i];
+
+               sz = struct_size(perfcntrs->groups[i], countables, group->num_counters);
+               perfcntrs->groups[i] = devm_kzalloc(dev, sz, GFP_KERNEL);
+               if (!perfcntrs->groups[i]) {
+                       __msm_perfcntr_cleanup(gpu, perfcntrs);
+                       return ERR_PTR(-ENOMEM);
+               }
+       }
+
+       return perfcntrs;
+}
index d73f1ad9039d8aad8992525b5932a0993443b047..b5a00ab7362b52a55e3a6f9c07f7710df399f5f1 100644 (file)
@@ -35,6 +35,29 @@ struct msm_perfcntr_group {
        const struct msm_perfcntr_counter *counters;
 };
 
+/**
+ * struct msm_perfcntr_stream - state for a single open stream fd
+ */
+struct msm_perfcntr_stream {
+       /** @gpu: Back-link to the GPU */
+       struct msm_gpu *gpu;
+
+       /** @nr_groups: # of counter groups with enabled counters */
+       uint32_t nr_groups;
+
+       /**
+        * @group_idx: array of nr_groups
+        *
+        * Maps the order of groups in PERFCNTR_CONFIG ioctl to group idx,
+        * so that results in the results stream can be ordered to match
+        * the ioctl call that setup the stream
+        */
+       uint32_t *group_idx;
+};
+
+uint32_t msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n);
+uint32_t msm_perfcntr_counter_base(const struct msm_perfcntr_stream *stream, uint32_t group_idx);
+
 /**
  * struct msm_perfcntr_context_state - per-msm_context counter state
  *