> On 08/07/2026 04:24, Guangliu Ding wrote:
> > Expose GPU utilization via /sys/kernel/debug/dri/<N>/gpu_load. The
> > file reports busy_time_ns, idle_time_ns and gpu_load %.
> >
> > Values are snapshotted at the end of each devfreq sampling window
> > (~50 ms) rather than read from live accumulators, avoiding load swings
> > over short observation intervals.
> >
> > The snapshot is cleared on suspend to avoid stale data when the GPU is
> > powered off.
> >
> > Signed-off-by: Guangliu Ding <[email protected]>
>
> Hi Guangliu,
>
> I'm puzzled exactly what you're trying to achieve with this. I can see that
> getting the GPU load might be useful, we do already have the 'fdinfo'
> mechanism
> to do this in a per-client manner but nothing good for "whole GPU"
> measurements.
>
> But stashing it in debugfs and formatting it as a string seems odd. Do you
> have a
> tool which uses this file, or is just for 'cat'ing to see the instantaneous
> load?
>
> Ideally we'd have something that would provide this information in a format
> that all drivers could implement, and not rely on debugfs files.
>
> Thanks,
> Steve
>
Hi Steve
Panthor currently exposes very limited runtime telemetry. The intent is to
expose utilization in this patch,
together with other device-wide GPU runtime metrics (frequency, shader
activity, draw-call counts,
memory usage, etc.) to a userspace tool for monitoring and
workload-optimization purposes.
fdinfo is primarily a per-client interface. Since it requires an open DRM fd and
reports information associated with that client, it does not map particularly
well to the global metrics exported by this patch.
If a generic cross-driver interface for this kind of GPU telemetry emerges,
I'm happy to switch to it. However, no such interface currently exists for
devfreq-based device utilization, so debugfs seemed like the most appropriate
existing option. Suggestions for a better interface are welcome.
Best Regards,
Guangliu
> > ---
> > Add /sys/kernel/debug/dri/<N>/gpu_load to expose GPU utilisation from
> > the last devfreq sampling window (~50 ms).
> > The file reports busy_time_ns, idle_time_ns and load percentage.
> >
> > Tested on NXP i.MX95 EVK (Mali-G310 V2).
> > ---
> > drivers/gpu/drm/panthor/panthor_devfreq.c | 80
> > +++++++++++++++++++++++++++++++
> drivers/gpu/drm/panthor/panthor_devfreq.h | 6 +++
> > drivers/gpu/drm/panthor/panthor_drv.c | 2 +
> > 3 files changed, 88 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c
> > b/drivers/gpu/drm/panthor/panthor_devfreq.c
> > index 2249b41ca4af..1b3f58ddfbd3 100644
> > --- a/drivers/gpu/drm/panthor/panthor_devfreq.c
> > +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
> > @@ -1,12 +1,16 @@
> > // SPDX-License-Identifier: GPL-2.0 or MIT
> > /* Copyright 2019 Collabora ltd. */
> > +/* Copyright 2026 NXP */
> >
> > #include <linux/clk.h>
> > +#include <linux/debugfs.h>
> > #include <linux/devfreq.h>
> > #include <linux/devfreq_cooling.h>
> > +#include <linux/math64.h>
> > #include <linux/platform_device.h>
> > #include <linux/pm_opp.h>
> >
> > +#include <drm/drm_file.h>
> > #include <drm/drm_managed.h>
> > #include <drm/drm_print.h>
> >
> > @@ -43,6 +47,22 @@ struct panthor_devfreq {
> > * and panthor_devfreq_record_{busy,idle}().
> > */
> > spinlock_t lock;
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > + /**
> > + * @last_busy_ns: Busy time in nanoseconds of the last completed
> devfreq window.
> > + * Updated by panthor_devfreq_get_dev_status() before resetting the
> counters.
> > + * Protected by @lock.
> > + */
> > + u64 last_busy_ns;
> > +
> > + /**
> > + * @last_total_ns: Total time in nanoseconds of the last completed
> devfreq window.
> > + * Updated by panthor_devfreq_get_dev_status() before resetting the
> counters.
> > + * Protected by @lock.
> > + */
> > + u64 last_total_ns;
> > +#endif
> > };
> >
> > static void panthor_devfreq_update_utilization(struct panthor_devfreq
> > *pdevfreq) @@ -101,6 +121,11 @@ static int
> > panthor_devfreq_get_dev_status(struct device *dev,
> >
> > status->busy_time = ktime_to_ns(pdevfreq->busy_time);
> >
> > +#ifdef CONFIG_DEBUG_FS
> > + pdevfreq->last_busy_ns = status->busy_time;
> > + pdevfreq->last_total_ns = status->total_time; #endif
> > +
> > panthor_devfreq_reset(pdevfreq);
> >
> > spin_unlock_irqrestore(&pdevfreq->lock, irqflags); @@ -283,6
> > +308,17 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev)
> > return;
> >
> > drm_WARN_ON(&ptdev->base,
> > devfreq_suspend_device(pdevfreq->devfreq));
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > + {
> > + unsigned long irqflags;
> > +
> > + spin_lock_irqsave(&pdevfreq->lock, irqflags);
> > + pdevfreq->last_busy_ns = 0;
> > + pdevfreq->last_total_ns = 0;
> > + spin_unlock_irqrestore(&pdevfreq->lock, irqflags);
> > + }
> > +#endif
> > }
> >
> > void panthor_devfreq_record_busy(struct panthor_device *ptdev) @@
> > -332,3 +368,47 @@ unsigned long panthor_devfreq_get_freq(struct
> > panthor_device *ptdev)
> >
> > return freq;
> > }
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > +static int panthor_devfreq_gpu_load_show(struct seq_file *m, void
> > +*unused) {
> > + struct panthor_device *ptdev = m->private;
> > + struct panthor_devfreq *pdevfreq = ptdev->devfreq;
> > + unsigned long irqflags;
> > + unsigned int gpu_load;
> > + u64 total_ns;
> > + u64 busy_ns;
> > +
> > + if (!pdevfreq->devfreq) {
> > + seq_puts(m, "devfreq not initialized\n");
> > + return 0;
> > + }
> > +
> > + spin_lock_irqsave(&pdevfreq->lock, irqflags);
> > + busy_ns = pdevfreq->last_busy_ns;
> > + total_ns = pdevfreq->last_total_ns;
> > + spin_unlock_irqrestore(&pdevfreq->lock, irqflags);
> > +
> > + busy_ns = min(busy_ns, total_ns);
> > + gpu_load = total_ns ? (unsigned int)div64_u64(busy_ns * 100ULL,
> > + total_ns) : 0;
> > +
> > + seq_printf(m, "busy_time_ns: %llu idle_time_ns: %llu
> gpu_load: %u%%\n",
> > + busy_ns, total_ns - busy_ns, gpu_load);
> > +
> > + return 0;
> > +}
> > +DEFINE_SHOW_ATTRIBUTE(panthor_devfreq_gpu_load);
> > +
> > +/**
> > + * panthor_devfreq_debugfs_init() - Initialize devfreq debugfs
> > +entries
> > + * @minor: DRM minor.
> > + */
> > +void panthor_devfreq_debugfs_init(struct drm_minor *minor) {
> > + struct panthor_device *ptdev = container_of(minor->dev,
> > + struct
> > +panthor_device, base);
> > +
> > + debugfs_create_file("gpu_load", 0444, minor->debugfs_root, ptdev,
> > + &panthor_devfreq_gpu_load_fops); } #endif
> /*
> > +CONFIG_DEBUG_FS */
> > diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.h
> > b/drivers/gpu/drm/panthor/panthor_devfreq.h
> > index f8e29e02f66c..4552569abfe4 100644
> > --- a/drivers/gpu/drm/panthor/panthor_devfreq.h
> > +++ b/drivers/gpu/drm/panthor/panthor_devfreq.h
> > @@ -1,5 +1,6 @@
> > /* SPDX-License-Identifier: GPL-2.0 or MIT */
> > /* Copyright 2019 Collabora ltd. */
> > +/* Copyright 2026 NXP */
> >
> > #ifndef __PANTHOR_DEVFREQ_H__
> > #define __PANTHOR_DEVFREQ_H__
> > @@ -20,4 +21,9 @@ void panthor_devfreq_record_idle(struct
> > panthor_device *ptdev);
> >
> > unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev);
> >
> > +#ifdef CONFIG_DEBUG_FS
> > +struct drm_minor;
> > +void panthor_devfreq_debugfs_init(struct drm_minor *minor); #endif
> > +
> > #endif /* __PANTHOR_DEVFREQ_H__ */
> > diff --git a/drivers/gpu/drm/panthor/panthor_drv.c
> > b/drivers/gpu/drm/panthor/panthor_drv.c
> > index e8dc4096c1d2..4093a6d21337 100644
> > --- a/drivers/gpu/drm/panthor/panthor_drv.c
> > +++ b/drivers/gpu/drm/panthor/panthor_drv.c
> > @@ -2,6 +2,7 @@
> > /* Copyright 2018 Marty E. Plummer <[email protected]> */
> > /* Copyright 2019 Linaro, Ltd., Rob Herring <[email protected]> */
> > /* Copyright 2019 Collabora ltd. */
> > +/* Copyright 2026 NXP */
> >
> > #ifdef CONFIG_ARM_ARCH_TIMER
> > #include <asm/arch_timer.h>
> > @@ -1761,6 +1762,7 @@ static void panthor_debugfs_init(struct
> > drm_minor *minor) {
> > panthor_mmu_debugfs_init(minor);
> > panthor_gem_debugfs_init(minor);
> > + panthor_devfreq_debugfs_init(minor);
> > }
> > #endif
> >
> >
> > ---
> > base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
> > change-id: 20260706-master-bd0eef99ca8e
> >
> > Best regards,
> > --
> > Guangliu Ding <[email protected]>
> >