* [PATCH v6 01/16] drm/panfrost: Move shrinker initialization and unplug one level down
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:34 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 02/16] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
` (14 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Since the moment we call drm_dev_register() the device should be in a
position to accept jobs, so it's best if the shrinker is already
initialized by then.
On top of that, make shrinker functions take an panfrost_device pointer
like other functions in the same sequence and rename them accordingly.
Essentially mimic the init/fini behaviour in Panthor.
On top of that, remove the config feature check, because it's unnecessary.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 8 +++++++-
drivers/gpu/drm/panfrost/panfrost_drv.c | 6 ------
drivers/gpu/drm/panfrost/panfrost_drv.h | 4 ++++
drivers/gpu/drm/panfrost/panfrost_gem.c | 25 ++++++++++++++----------
drivers/gpu/drm/panfrost/panfrost_gem.h | 7 ++++---
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 8 ++------
6 files changed, 32 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 485349faf251..05c40d5a20b5 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -280,9 +280,14 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_job;
- panfrost_gem_init(pfdev);
+ err = panfrost_gem_init(pfdev);
+ if (err)
+ goto out_perfcnt;
return 0;
+
+out_perfcnt:
+ panfrost_perfcnt_fini(pfdev);
out_job:
panfrost_jm_fini(pfdev);
out_mmu:
@@ -304,6 +309,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
+ panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
panfrost_mmu_fini(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 96a66d673ba6..9882a3ede75f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -1003,14 +1003,9 @@ static int panfrost_probe(struct platform_device *pdev)
if (err < 0)
goto err_out1;
- err = panfrost_gem_shrinker_init(&pfdev->base);
- if (err)
- goto err_out2;
return 0;
-err_out2:
- drm_dev_unregister(&pfdev->base);
err_out1:
pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
@@ -1024,7 +1019,6 @@ static void panfrost_remove(struct platform_device *pdev)
struct panfrost_device *pfdev = platform_get_drvdata(pdev);
drm_dev_unregister(&pfdev->base);
- panfrost_gem_shrinker_cleanup(&pfdev->base);
pm_runtime_get_sync(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
index a2277ec61aab..bac0c415d4da 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.h
@@ -4,6 +4,10 @@
#ifndef __PANFROST_DRV_H__
#define __PANFROST_DRV_H__
+#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
extern bool panfrost_transparent_hugepage;
+#else
+#define panfrost_transparent_hugepage false
+#endif
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3a7fce428898..7b993a089af2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -15,20 +15,25 @@
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
-void panfrost_gem_init(struct panfrost_device *pfdev)
+int panfrost_gem_init(struct panfrost_device *pfdev)
{
int err;
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
- !panfrost_transparent_hugepage)
- return;
+ if (panfrost_transparent_hugepage) {
+ err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
+ if (drm_gem_get_huge_mnt(&pfdev->base))
+ drm_info(&pfdev->base, "Using Transparent Hugepage\n");
+ else if (err)
+ drm_warn(&pfdev->base,
+ "Can't use Transparent Hugepage (%d)\n", err);
+ }
- err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
- if (drm_gem_get_huge_mnt(&pfdev->base))
- drm_info(&pfdev->base, "Using Transparent Hugepage\n");
- else if (err)
- drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n",
- err);
+ return panfrost_gem_shrinker_init(pfdev);
+}
+
+void panfrost_gem_fini(struct panfrost_device *pfdev)
+{
+ panfrost_gem_shrinker_fini(pfdev);
}
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 79d4377019e9..5c823cdbd980 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
return container_of(node, struct panfrost_gem_mapping, mmnode);
}
-void panfrost_gem_init(struct panfrost_device *pfdev);
+int panfrost_gem_init(struct panfrost_device *pfdev);
+void panfrost_gem_fini(struct panfrost_device *pfdev);
struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
@@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
-int panfrost_gem_shrinker_init(struct drm_device *dev);
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev);
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev);
void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label);
int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 2fe967a90bcb..fefae87535d6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -95,10 +95,8 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*
* This function registers and sets up the panfrost shrinker.
*/
-int panfrost_gem_shrinker_init(struct drm_device *dev)
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
-
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
return -ENOMEM;
@@ -118,10 +116,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
*
* This function unregisters the panfrost shrinker.
*/
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
-
if (pfdev->shrinker)
shrinker_free(pfdev->shrinker);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 01/16] drm/panfrost: Move shrinker initialization and unplug one level down
2026-08-26 21:18 ` [PATCH v6 01/16] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-08-26 21:34 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:34 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Closing a GEM object or MMU context during/after device unbind causes a kernel panic due to unclocked MMU register access.
--
commit 78f80dbfd319f079f4898f5d8b2af5e44a1877d5
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Move shrinker initialization and unplug one level down
This commit moves the initialization and cleanup of the GEM shrinker into
the core GEM init and fini paths. It mimics the behavior seen in Panthor
and removes an unnecessary config feature check.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
[ ... ]
> @@ -1024,7 +1019,6 @@ static void panfrost_remove(struct platform_device *pdev)
> struct panfrost_device *pfdev = platform_get_drvdata(pdev);
>
> drm_dev_unregister(&pfdev->base);
> - panfrost_gem_shrinker_cleanup(&pfdev->base);
>
> pm_runtime_get_sync(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
[Severity: High]
This is a pre-existing issue, but closing a GEM object or MMU context during
or after device unbind can cause a kernel panic due to unclocked MMU register
access.
When a userspace process closes a DRM file descriptor concurrently with
device unbind, it triggers panfrost_mmu_flush_range().
During unbind, pm_runtime_disable() is called here while the device is in
the RPM_ACTIVE state. The status remains RPM_ACTIVE even after
panfrost_clk_fini() turns off the hardware clocks.
Because the status remains active, concurrent execution of
panfrost_mmu_flush_range() evaluates to true:
drivers/gpu/drm/panfrost/panfrost_mmu.c:panfrost_mmu_flush_range() {
...
if (pm_runtime_active(pfdev->base.dev))
mmu_hw_do_operation(pfdev, mmu, iova, size, AS_COMMAND_FLUSH_PT);
...
}
The same vulnerability exists in context destruction during teardown:
drivers/gpu/drm/panfrost/panfrost_mmu.c:panfrost_mmu_release_ctx() {
...
if (pm_runtime_active(pfdev->base.dev))
panfrost_mmu_disable(pfdev, mmu->as);
...
}
Could this pm_runtime_disable() sequence leave the device vulnerable to a
synchronous external abort on ARM when those powered-down registers are
concurrently accessed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=1
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 02/16] drm/panfrost: Move all DRM device initialisation into device_init()
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 01/16] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 03/16] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
` (13 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Ideally the probe() function will do as little as possible, and all device
initialisation and registration should happen inside the panfrost device
subsystem, just like it's done in Panthor. This also simplifies resource
unwinding in the error path.
Do the same thing for DRM driver remove, as in, sweep most of the action
into panfrost_device_fini(), just like we did for device probe.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 33 ++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_drv.c | 44 +-----------------------------
2 files changed, 34 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 05c40d5a20b5..d2d2830f11a7 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -8,6 +8,7 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <drm/drm_drv.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -216,6 +217,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
{
int err;
+ pfdev->comp = of_device_get_match_data(pfdev->base.dev);
+ if (!pfdev->comp)
+ return -ENODEV;
+
+ pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
+
+ mutex_init(&pfdev->shrinker_lock);
+ INIT_LIST_HEAD(&pfdev->shrinker_list);
+
mutex_init(&pfdev->sched_lock);
INIT_LIST_HEAD(&pfdev->as_lru_list);
@@ -284,8 +294,25 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_perfcnt;
+ pm_runtime_set_active(pfdev->base.dev);
+ pm_runtime_mark_last_busy(pfdev->base.dev);
+ pm_runtime_enable(pfdev->base.dev);
+ pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
+ pm_runtime_use_autosuspend(pfdev->base.dev);
+
+ /*
+ * Register the DRM device with the core and the connectors with
+ * sysfs
+ */
+ err = drm_dev_register(&pfdev->base, 0);
+ if (err < 0)
+ goto out_devreg;
+
return 0;
+out_devreg:
+ pm_runtime_disable(pfdev->base.dev);
+ panfrost_gem_fini(pfdev);
out_perfcnt:
panfrost_perfcnt_fini(pfdev);
out_job:
@@ -304,11 +331,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
+ pm_runtime_set_suspended(pfdev->base.dev);
return err;
}
void panfrost_device_fini(struct panfrost_device *pfdev)
{
+ pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
+
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
@@ -319,6 +350,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_clk_fini(pfdev);
panfrost_reset_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
+
+ pm_runtime_set_suspended(pfdev->base.dev);
}
#define PANFROST_EXCEPTION(id) \
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 9882a3ede75f..80996e311a9d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -964,7 +964,6 @@ MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with T
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
- int err;
pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
struct panfrost_device, base);
@@ -973,45 +972,7 @@ static int panfrost_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pfdev);
- pfdev->comp = of_device_get_match_data(&pdev->dev);
- if (!pfdev->comp)
- return -ENODEV;
-
- pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
-
- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
- err = panfrost_device_init(pfdev);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Fatal error during GPU init\n");
- goto err_out0;
- }
-
- pm_runtime_set_active(pfdev->base.dev);
- pm_runtime_mark_last_busy(pfdev->base.dev);
- pm_runtime_enable(pfdev->base.dev);
- pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
- pm_runtime_use_autosuspend(pfdev->base.dev);
-
- /*
- * Register the DRM device with the core and the connectors with
- * sysfs
- */
- err = drm_dev_register(&pfdev->base, 0);
- if (err < 0)
- goto err_out1;
-
-
- return 0;
-
-err_out1:
- pm_runtime_disable(pfdev->base.dev);
- panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
-err_out0:
- return err;
+ return panfrost_device_init(pfdev);
}
static void panfrost_remove(struct platform_device *pdev)
@@ -1020,10 +981,7 @@ static void panfrost_remove(struct platform_device *pdev)
drm_dev_unregister(&pfdev->base);
- pm_runtime_get_sync(pfdev->base.dev);
- pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
}
static ssize_t profiling_show(struct device *dev,
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 03/16] drm/panfrost: Move lock and modparam initialisations into their subsystems
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 01/16] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 02/16] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 04/16] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
` (12 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The device init function feels a bit cluttered with things that should be
done in the specific subsystems where those locks and lists are being
used. Same goes for module parameters, it's best to limit them to the
subsystems where they're relevant.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 10 ----------
drivers/gpu/drm/panfrost/panfrost_drv.c | 7 -------
drivers/gpu/drm/panfrost/panfrost_drv.h | 13 -------------
drivers/gpu/drm/panfrost/panfrost_gem.c | 9 ++++++++-
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 3 +++
drivers/gpu/drm/panfrost/panfrost_gpu.c | 2 ++
drivers/gpu/drm/panfrost/panfrost_job.c | 1 +
drivers/gpu/drm/panfrost/panfrost_mmu.c | 3 +++
8 files changed, 17 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index d2d2830f11a7..7daa2143f3c4 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -223,16 +223,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
- mutex_init(&pfdev->sched_lock);
- INIT_LIST_HEAD(&pfdev->as_lru_list);
-
- spin_lock_init(&pfdev->as_lock);
-
- spin_lock_init(&pfdev->cycle_counter.lock);
-
#ifdef CONFIG_DEBUG_FS
mutex_init(&pfdev->debugfs.gems_lock);
INIT_LIST_HEAD(&pfdev->debugfs.gems_list);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 80996e311a9d..ec8c1c08e147 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -21,7 +21,6 @@
#include <drm/drm_utils.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
#include "panfrost_job.h"
@@ -955,12 +954,6 @@ static const struct drm_driver panfrost_drm_driver = {
#endif
};
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-bool panfrost_transparent_hugepage = true;
-module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
-MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
-#endif
-
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
deleted file mode 100644
index bac0c415d4da..000000000000
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 or MIT */
-/* Copyright 2025 Amazon.com, Inc. or its affiliates */
-
-#ifndef __PANFROST_DRV_H__
-#define __PANFROST_DRV_H__
-
-#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
-extern bool panfrost_transparent_hugepage;
-#else
-#define panfrost_transparent_hugepage false
-#endif
-
-#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 7b993a089af2..15105b7f954b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -11,10 +11,17 @@
#include <drm/panfrost_drm.h>
#include <drm/drm_print.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+bool panfrost_transparent_hugepage = true;
+module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
+MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
+#else
+#define panfrost_transparent_hugepage false
+#endif
+
int panfrost_gem_init(struct panfrost_device *pfdev)
{
int err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index fefae87535d6..72cd7a165e6d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -97,6 +97,9 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*/
int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
{
+ mutex_init(&pfdev->shrinker_lock);
+ INIT_LIST_HEAD(&pfdev->shrinker_list);
+
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
return -ENOMEM;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 7d555e63e21a..8a15ccce08e9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -516,6 +516,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
{
int err;
+ spin_lock_init(&pfdev->cycle_counter.lock);
+
err = panfrost_gpu_soft_reset(pfdev);
if (err)
return err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 2d12b83e900a..6f920fd0fc4e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -873,6 +873,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
+ mutex_init(&pfdev->sched_lock);
js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
if (js->irq < 0)
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 4a3162c3b659..5c393ed6e310 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -955,6 +955,9 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
{
int err;
+ INIT_LIST_HEAD(&pfdev->as_lru_list);
+ spin_lock_init(&pfdev->as_lock);
+
pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
if (pfdev->mmu_irq < 0)
return pfdev->mmu_irq;
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 04/16] drm/panfrost: Move debugfs initialisation to relevant subsystems
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (2 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 03/16] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 05/16] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
` (11 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Because debugfs knobs are a device interface, and also the different knobs
deal with specific subsystems, it's best to move their initialisations away
from panfrost_drv.c and into the subsystem they provide information about.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 8 ++
drivers/gpu/drm/panfrost/panfrost_device.h | 4 +
drivers/gpu/drm/panfrost/panfrost_drv.c | 129 +----------------------------
drivers/gpu/drm/panfrost/panfrost_gem.c | 29 ++++++-
drivers/gpu/drm/panfrost/panfrost_gem.h | 3 +-
drivers/gpu/drm/panfrost/panfrost_job.c | 99 ++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_job.h | 4 +
7 files changed, 144 insertions(+), 132 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 7daa2143f3c4..6e3fcd53b690 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -591,3 +591,11 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
RUNTIME_PM_OPS(panfrost_device_runtime_suspend, panfrost_device_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(panfrost_device_suspend, panfrost_device_resume)
};
+
+#ifdef CONFIG_DEBUG_FS
+void panfrost_device_debugfs_init(struct drm_minor *minor)
+{
+ panfrost_gems_debugfs_init(minor);
+ panfrost_sched_debugfs_init(minor);
+}
+#endif // CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 1fe1554f2652..a0b9a2145fc9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -341,4 +341,8 @@ panfrost_device_schedule_reset(struct panfrost_device *pfdev)
queue_work(pfdev->reset.wq, &pfdev->reset.work);
}
+#ifdef CONFIG_DEBUG_FS
+void panfrost_device_debugfs_init(struct drm_minor *minor);
+#endif // CONFIG_DEBUG_FS
+
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index ec8c1c08e147..f77780c72a1a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -792,133 +792,6 @@ static const struct file_operations panfrost_drm_driver_fops = {
.show_fdinfo = drm_show_fdinfo,
};
-#ifdef CONFIG_DEBUG_FS
-static int panthor_gems_show(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = m->private;
- struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
-
- panfrost_gem_debugfs_print_bos(pfdev, m);
-
- return 0;
-}
-
-static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
- struct seq_file *m)
-{
- struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
- const char *prio = "UNKNOWN";
-
- static const char * const prios[] = {
- [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
- [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
- [DRM_SCHED_PRIORITY_LOW] = "LOW",
- };
-
- if (jm_ctx->slot_entity[0].priority !=
- jm_ctx->slot_entity[1].priority)
- drm_warn(ddev, "Slot priorities should be the same in a single context");
-
- if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
- prio = prios[jm_ctx->slot_entity[0].priority];
-
- seq_printf(m, " JM context %u: priority %s\n", handle, prio);
-}
-
-static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
- struct seq_file *m)
-{
- struct panfrost_jm_ctx *jm_ctx;
- unsigned long i;
-
- xa_lock(&pfile->jm_ctxs);
- xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
- jm_ctx = panfrost_jm_ctx_get(jm_ctx);
- xa_unlock(&pfile->jm_ctxs);
- show_panfrost_jm_ctx(jm_ctx, i, m);
- panfrost_jm_ctx_put(jm_ctx);
- xa_lock(&pfile->jm_ctxs);
- }
- xa_unlock(&pfile->jm_ctxs);
-
- return 0;
-}
-
-static struct drm_info_list panthor_debugfs_list[] = {
- {"gems",
- panthor_gems_show, 0, NULL},
-};
-
-static int panthor_gems_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panthor_debugfs_list,
- ARRAY_SIZE(panthor_debugfs_list),
- minor->debugfs_root, minor);
-
- return 0;
-}
-
-static int show_each_file(struct seq_file *m, void *arg)
-{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *ddev = node->minor->dev;
- int (*show)(struct panfrost_file_priv *, struct seq_file *) =
- node->info_ent->data;
- struct drm_file *file;
- int ret;
-
- ret = mutex_lock_interruptible(&ddev->filelist_mutex);
- if (ret)
- return ret;
-
- list_for_each_entry(file, &ddev->filelist, lhead) {
- struct task_struct *task;
- struct panfrost_file_priv *pfile = file->driver_priv;
- struct pid *pid;
-
- /*
- * Although we have a valid reference on file->pid, that does
- * not guarantee that the task_struct who called get_pid() is
- * still alive (e.g. get_pid(current) => fork() => exit()).
- * Therefore, we need to protect this ->comm access using RCU.
- */
- rcu_read_lock();
- pid = rcu_dereference(file->pid);
- task = pid_task(pid, PIDTYPE_TGID);
- seq_printf(m, "client_id %8llu pid %8d command %s:\n",
- file->client_id, pid_nr(pid),
- task ? task->comm : "<unknown>");
- rcu_read_unlock();
-
- ret = show(pfile, m);
- if (ret < 0)
- break;
-
- seq_puts(m, "\n");
- }
-
- mutex_unlock(&ddev->filelist_mutex);
- return ret;
-}
-
-static struct drm_info_list panfrost_sched_debugfs_list[] = {
- { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
-};
-
-static void panfrost_sched_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panfrost_sched_debugfs_list,
- ARRAY_SIZE(panfrost_sched_debugfs_list),
- minor->debugfs_root, minor);
-}
-
-static void panfrost_debugfs_init(struct drm_minor *minor)
-{
- panthor_gems_debugfs_init(minor);
- panfrost_sched_debugfs_init(minor);
-}
-#endif
-
/*
* Panfrost driver version:
* - 1.0 - initial interface
@@ -950,7 +823,7 @@ static const struct drm_driver panfrost_drm_driver = {
.gem_prime_import = panfrost_gem_prime_import,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
#ifdef CONFIG_DEBUG_FS
- .debugfs_init = panfrost_debugfs_init,
+ .debugfs_init = panfrost_device_debugfs_init,
#endif
};
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 15105b7f954b..54717fdb8624 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -9,6 +9,7 @@
#include <linux/dma-mapping.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_debugfs.h>
#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_gem.h"
@@ -736,8 +737,8 @@ static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo,
totals->reclaimable += resident_size;
}
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m)
+static void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
+ struct seq_file *m)
{
struct gem_size_totals totals = {0};
struct panfrost_gem_object *bo;
@@ -757,4 +758,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
totals.size, totals.resident, totals.reclaimable);
}
+
+static int panfrost_gems_show(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
+
+ panfrost_gem_debugfs_print_bos(pfdev, m);
+
+ return 0;
+}
+
+static struct drm_info_list panfrost_debugfs_list[] = {
+ {"gems",
+ panfrost_gems_show, 0, NULL},
+};
+
+int panfrost_gems_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_debugfs_list,
+ ARRAY_SIZE(panfrost_debugfs_list),
+ minor->debugfs_root, minor);
+
+ return 0;
+}
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 5c823cdbd980..49bb5691087e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -164,8 +164,7 @@ int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label);
#ifdef CONFIG_DEBUG_FS
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m);
+int panfrost_gems_debugfs_init(struct drm_minor *minor);
#endif
#endif /* __PANFROST_GEM_H__ */
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 6f920fd0fc4e..5016d2d53da2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -8,6 +8,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <drm/panfrost_drm.h>
@@ -1137,3 +1139,100 @@ int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle)
panfrost_jm_ctx_put(jm_ctx);
return 0;
}
+
+#ifdef CONFIG_DEBUG_FS
+static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
+ struct seq_file *m)
+{
+ struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
+ const char *prio = "UNKNOWN";
+
+ static const char * const prios[] = {
+ [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
+ [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
+ [DRM_SCHED_PRIORITY_LOW] = "LOW",
+ };
+
+ if (jm_ctx->slot_entity[0].priority !=
+ jm_ctx->slot_entity[1].priority)
+ drm_warn(ddev, "Slot priorities should be the same in a single context");
+
+ if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
+ prio = prios[jm_ctx->slot_entity[0].priority];
+
+ seq_printf(m, " JM context %u: priority %s\n", handle, prio);
+}
+
+static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
+ struct seq_file *m)
+{
+ struct panfrost_jm_ctx *jm_ctx;
+ unsigned long i;
+
+ xa_lock(&pfile->jm_ctxs);
+ xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
+ jm_ctx = panfrost_jm_ctx_get(jm_ctx);
+ xa_unlock(&pfile->jm_ctxs);
+ show_panfrost_jm_ctx(jm_ctx, i, m);
+ panfrost_jm_ctx_put(jm_ctx);
+ xa_lock(&pfile->jm_ctxs);
+ }
+ xa_unlock(&pfile->jm_ctxs);
+
+ return 0;
+}
+
+static int show_each_file(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *ddev = node->minor->dev;
+ int (*show)(struct panfrost_file_priv *, struct seq_file *) =
+ node->info_ent->data;
+ struct drm_file *file;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ddev->filelist_mutex);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(file, &ddev->filelist, lhead) {
+ struct task_struct *task;
+ struct panfrost_file_priv *pfile = file->driver_priv;
+ struct pid *pid;
+
+ /*
+ * Although we have a valid reference on file->pid, that does
+ * not guarantee that the task_struct who called get_pid() is
+ * still alive (e.g. get_pid(current) => fork() => exit()).
+ * Therefore, we need to protect this ->comm access using RCU.
+ */
+ rcu_read_lock();
+ pid = rcu_dereference(file->pid);
+ task = pid_task(pid, PIDTYPE_TGID);
+ seq_printf(m, "client_id %8llu pid %8d command %s:\n",
+ file->client_id, pid_nr(pid),
+ task ? task->comm : "<unknown>");
+ rcu_read_unlock();
+
+ ret = show(pfile, m);
+ if (ret < 0)
+ break;
+
+ seq_puts(m, "\n");
+ }
+
+ mutex_unlock(&ddev->filelist_mutex);
+ return ret;
+}
+
+static struct drm_info_list panfrost_sched_debugfs_list[] = {
+ { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
+};
+
+void panfrost_sched_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_sched_debugfs_list,
+ ARRAY_SIZE(panfrost_sched_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index c3f57e41a571..3e3b9717838f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -74,4 +74,8 @@ int panfrost_job_get_slot(struct panfrost_job *job);
int panfrost_job_push(struct panfrost_job *job);
void panfrost_job_put(struct panfrost_job *job);
+#ifdef CONFIG_DEBUG_FS
+void panfrost_sched_debugfs_init(struct drm_minor *minor);
+#endif
+
#endif
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 05/16] drm/panfrost: Skip NULL checks for clock enable/disabling
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (3 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 04/16] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 06/16] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
` (10 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The clk_* API can deal with NULL clock objects, so we can do away with NULL
checks in client code.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 6e3fcd53b690..a9d9669912e0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -540,11 +540,9 @@ static int panfrost_device_resume(struct device *dev)
if (ret)
goto err_clk;
- if (pfdev->bus_clock) {
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
- }
+ ret = clk_enable(pfdev->bus_clock);
+ if (ret)
+ goto err_bus_clk;
}
ret = pm_runtime_force_resume(dev);
@@ -554,7 +552,7 @@ static int panfrost_device_resume(struct device *dev)
return 0;
err_resume:
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock)
+ if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
clk_disable(pfdev->bus_clock);
err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
@@ -575,9 +573,7 @@ static int panfrost_device_suspend(struct device *dev)
return ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- if (pfdev->bus_clock)
- clk_disable(pfdev->bus_clock);
-
+ clk_disable(pfdev->bus_clock);
clk_disable(pfdev->clock);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 06/16] drm/panfrost: Consolidate device clock management and reset
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (4 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 05/16] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 07/16] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
` (9 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Gather all clock enables and disables into a single function to avoid
repetition. Also, as part of the same function, handle reset control
(de)assertions, since that was already the case in the PM runtime
functions.
Also do clk (un)prepares and dis/enables at the same time, since the
clk_prepare_* family of functions can simply increase the refcnt of
an already prepared clock.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 123 +++++++++++++----------------
1 file changed, 53 insertions(+), 70 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index a9d9669912e0..9549ddb7854a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -27,18 +27,53 @@ static int panfrost_reset_init(struct panfrost_device *pfdev)
return PTR_ERR(pfdev->rstc);
}
- return reset_control_deassert(pfdev->rstc);
+ return 0;
+}
+
+static int panfrost_clk_enable_deassert_reset(struct panfrost_device *pfdev)
+{
+ int err;
+
+ err = reset_control_deassert(pfdev->rstc);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(pfdev->clock);
+ if (err)
+ goto assert_reset;
+
+ err = clk_prepare_enable(pfdev->bus_clock);
+ if (err)
+ goto disable_clock;
+
+ err = clk_prepare_enable(pfdev->bus_ace_clock);
+ if (err)
+ goto disable_bus_clock;
+
+ return 0;
+
+disable_bus_clock:
+ clk_disable_unprepare(pfdev->bus_clock);
+disable_clock:
+ clk_disable_unprepare(pfdev->clock);
+assert_reset:
+ reset_control_assert(pfdev->rstc);
+
+ return err;
}
-static void panfrost_reset_fini(struct panfrost_device *pfdev)
+static void panfrost_clk_disable_assert_reset(struct panfrost_device *pfdev)
{
+ clk_disable_unprepare(pfdev->bus_ace_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
reset_control_assert(pfdev->rstc);
}
static int panfrost_clk_init(struct panfrost_device *pfdev)
{
- int err;
unsigned long rate;
+ int err = 0;
pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
if (IS_ERR(pfdev->clock)) {
@@ -49,53 +84,31 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
rate = clk_get_rate(pfdev->clock);
dev_info(pfdev->base.dev, "clock rate = %lu\n", rate);
- err = clk_prepare_enable(pfdev->clock);
- if (err)
- return err;
-
pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus");
if (IS_ERR(pfdev->bus_clock)) {
- dev_err(pfdev->base.dev, "get bus_clock failed %ld\n",
- PTR_ERR(pfdev->bus_clock));
err = PTR_ERR(pfdev->bus_clock);
- goto disable_clock;
+ dev_err(pfdev->base.dev, "get bus_clock failed %d\n", err);
+ return err;
}
if (pfdev->bus_clock) {
rate = clk_get_rate(pfdev->bus_clock);
dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate);
-
- err = clk_prepare_enable(pfdev->bus_clock);
- if (err)
- goto disable_clock;
}
pfdev->bus_ace_clock = devm_clk_get_optional(pfdev->base.dev, "bus_ace");
if (IS_ERR(pfdev->bus_ace_clock)) {
err = PTR_ERR(pfdev->bus_ace_clock);
dev_err(pfdev->base.dev, "get bus_ace_clock failed %d\n", err);
- goto disable_bus_clock;
+ return err;
}
- err = clk_prepare_enable(pfdev->bus_ace_clock);
- if (err)
- goto disable_bus_clock;
-
- return 0;
-
-disable_bus_clock:
- clk_disable_unprepare(pfdev->bus_clock);
-disable_clock:
- clk_disable_unprepare(pfdev->clock);
-
- return err;
+ return panfrost_clk_enable_deassert_reset(pfdev);
}
static void panfrost_clk_fini(struct panfrost_device *pfdev)
{
- clk_disable_unprepare(pfdev->bus_ace_clock);
- clk_disable_unprepare(pfdev->bus_clock);
- clk_disable_unprepare(pfdev->clock);
+ panfrost_clk_disable_assert_reset(pfdev);
}
static int panfrost_regulator_init(struct panfrost_device *pfdev)
@@ -241,7 +254,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
err = panfrost_clk_init(pfdev);
if (err) {
dev_err(pfdev->base.dev, "clk init failed %d\n", err);
- goto out_reset;
+ goto out_pm_domain;
}
err = panfrost_devfreq_init(pfdev);
@@ -317,8 +330,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
panfrost_devfreq_fini(pfdev);
out_clk:
panfrost_clk_fini(pfdev);
-out_reset:
- panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
@@ -338,7 +349,6 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_devfreq_fini(pfdev);
panfrost_regulator_fini(pfdev);
panfrost_clk_fini(pfdev);
- panfrost_reset_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
@@ -462,38 +472,15 @@ static int panfrost_device_runtime_resume(struct device *dev)
int ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- ret = reset_control_deassert(pfdev->rstc);
+ ret = panfrost_clk_enable_deassert_reset(pfdev);
if (ret)
return ret;
-
- ret = clk_enable(pfdev->clock);
- if (ret)
- goto err_clk;
-
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
-
- ret = clk_enable(pfdev->bus_ace_clock);
- if (ret)
- goto err_bus_ace_clk;
}
panfrost_device_reset(pfdev, true);
panfrost_devfreq_resume(pfdev);
return 0;
-
-err_bus_ace_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->bus_clock);
-err_bus_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->clock);
-err_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- reset_control_assert(pfdev->rstc);
- return ret;
}
static int panfrost_device_runtime_suspend(struct device *dev)
@@ -509,12 +496,8 @@ static int panfrost_device_runtime_suspend(struct device *dev)
panfrost_gpu_suspend_irq(pfdev);
panfrost_gpu_power_off(pfdev);
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- clk_disable(pfdev->bus_ace_clock);
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
- reset_control_assert(pfdev->rstc);
- }
+ if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
+ panfrost_clk_disable_assert_reset(pfdev);
return 0;
}
@@ -536,11 +519,11 @@ static int panfrost_device_resume(struct device *dev)
}
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- ret = clk_enable(pfdev->clock);
+ ret = clk_prepare_enable(pfdev->clock);
if (ret)
goto err_clk;
- ret = clk_enable(pfdev->bus_clock);
+ ret = clk_prepare_enable(pfdev->bus_clock);
if (ret)
goto err_bus_clk;
}
@@ -553,10 +536,10 @@ static int panfrost_device_resume(struct device *dev)
err_resume:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->clock);
+ clk_disable_unprepare(pfdev->clock);
err_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
@@ -573,8 +556,8 @@ static int panfrost_device_suspend(struct device *dev)
return ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
}
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 07/16] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (5 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 06/16] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:34 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 08/16] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
` (8 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Because MMU interrupts are only enabled when the device is reset, it
happened that after DRM device registration, the very first job targeting
the tiler heap BO would always time out. The reason is the reset sequence
is only part of PM runtime resume, which is not called explicitly at driver
probe time, and an actual reset work item manually triggered after a HW
error.
I have attempted a somewhat drastic solution, which is completely
decoupling GPU/MMU/JM subsystem initialisation and reset from interrupt
enablement, so that we can handle IRQ toggling a bit more flexibly.
To this end:
- Ensure every subsystem with its own IRQ has an 'enable interrupts'
method, and that it doesn't enable them anywhere else.
- Force IRQ masking at MMU reset time. Up until, now, panfrost_mmu_reset()
was clearing the MMU IRQ suspension bit, but at no point that is set during
the reset sequence.
Then manually enable all interrupts when the device is fully initialised at
probe time, right before DRM device registration, or after the reset
sequence is complete. Also disable all interrupts at device remove time,
so that their IRQs can be sync'ed right before tearing the device down.
Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 41 ++++++++++++++++++++++--------
drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++-
drivers/gpu/drm/panfrost/panfrost_gpu.c | 19 ++++++++------
drivers/gpu/drm/panfrost/panfrost_gpu.h | 2 ++
drivers/gpu/drm/panfrost/panfrost_job.c | 7 +++--
drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 +++++--
drivers/gpu/drm/panfrost/panfrost_mmu.h | 2 ++
7 files changed, 57 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 9549ddb7854a..0cc277efb585 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -226,6 +226,27 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
return err;
}
+void panfrost_device_enable_int(struct panfrost_device *pfdev)
+{
+ panfrost_gpu_enable_interrupts(pfdev);
+ panfrost_mmu_enable_interrupts(pfdev);
+ panfrost_jm_enable_interrupts(pfdev);
+}
+
+static void panfrost_device_enable_hw(struct panfrost_device *pfdev)
+{
+ panfrost_device_enable_int(pfdev);
+ panfrost_devfreq_resume(pfdev);
+}
+
+static void panfrost_device_disable_hw(struct panfrost_device *pfdev)
+{
+ panfrost_devfreq_suspend(pfdev);
+ panfrost_jm_suspend_irq(pfdev);
+ panfrost_mmu_suspend_irq(pfdev);
+ panfrost_gpu_suspend_irq(pfdev);
+}
+
int panfrost_device_init(struct panfrost_device *pfdev)
{
int err;
@@ -297,6 +318,8 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_perfcnt;
+ panfrost_device_enable_hw(pfdev);
+
pm_runtime_set_active(pfdev->base.dev);
pm_runtime_mark_last_busy(pfdev->base.dev);
pm_runtime_enable(pfdev->base.dev);
@@ -315,6 +338,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
out_devreg:
pm_runtime_disable(pfdev->base.dev);
+ panfrost_device_disable_hw(pfdev);
panfrost_gem_fini(pfdev);
out_perfcnt:
panfrost_perfcnt_fini(pfdev);
@@ -341,6 +365,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
pm_runtime_get_sync(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
+ panfrost_device_disable_hw(pfdev);
+
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
@@ -454,16 +480,12 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
return false;
}
-void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
+void panfrost_device_reset(struct panfrost_device *pfdev)
{
panfrost_gpu_soft_reset(pfdev);
-
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
-
panfrost_jm_reset_interrupts(pfdev);
- if (enable_job_int)
- panfrost_jm_enable_interrupts(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
@@ -477,8 +499,8 @@ static int panfrost_device_runtime_resume(struct device *dev)
return ret;
}
- panfrost_device_reset(pfdev, true);
- panfrost_devfreq_resume(pfdev);
+ panfrost_device_reset(pfdev);
+ panfrost_device_enable_hw(pfdev);
return 0;
}
@@ -490,10 +512,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
if (!panfrost_jm_is_idle(pfdev))
return -EBUSY;
- panfrost_devfreq_suspend(pfdev);
- panfrost_jm_suspend_irq(pfdev);
- panfrost_mmu_suspend_irq(pfdev);
- panfrost_gpu_suspend_irq(pfdev);
+ panfrost_device_disable_hw(pfdev);
panfrost_gpu_power_off(pfdev);
if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index a0b9a2145fc9..c94546b49662 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -250,7 +250,8 @@ int panfrost_unstable_ioctl_check(void);
int panfrost_device_init(struct panfrost_device *pfdev);
void panfrost_device_fini(struct panfrost_device *pfdev);
-void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int);
+void panfrost_device_enable_int(struct panfrost_device *pfdev);
+void panfrost_device_reset(struct panfrost_device *pfdev);
extern const struct dev_pm_ops panfrost_pm_ops;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 8a15ccce08e9..c8e0b1acc669 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -67,8 +67,6 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
gpu_write(pfdev, GPU_INT_MASK, 0);
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED);
- clear_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
-
gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET);
ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT,
val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000);
@@ -87,12 +85,6 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_MASK_ALL);
- /* Only enable the interrupts we care about */
- gpu_write(pfdev, GPU_INT_MASK,
- GPU_IRQ_MASK_ERROR |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED |
- GPU_IRQ_CLEAN_CACHES_COMPLETED);
-
/*
* All in-flight jobs should have released their cycle
* counter references upon reset, but let us make sure
@@ -504,6 +496,17 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
dev_err(pfdev->base.dev, "l2 power transition timeout");
}
+void panfrost_gpu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ clear_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
+
+ /* Only enable the interrupts we care about */
+ gpu_write(pfdev, GPU_INT_MASK,
+ GPU_IRQ_MASK_ERROR |
+ GPU_IRQ_PERFCNT_SAMPLE_COMPLETED |
+ GPU_IRQ_CLEAN_CACHES_COMPLETED);
+}
+
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
{
set_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
index b4fef11211d5..743d45b00d9f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
@@ -15,6 +15,8 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev);
int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
void panfrost_gpu_power_on(struct panfrost_device *pfdev);
void panfrost_gpu_power_off(struct panfrost_device *pfdev);
+
+void panfrost_gpu_enable_interrupts(struct panfrost_device *pfdev);
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);
void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 5016d2d53da2..087905cd347a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -732,7 +732,7 @@ panfrost_reset(struct panfrost_device *pfdev,
spin_unlock(&pfdev->js->job_lock);
/* Proceed with reset now. */
- panfrost_device_reset(pfdev, false);
+ panfrost_device_reset(pfdev);
/* GPU has been reset, we can clear the reset pending bit. */
atomic_set(&pfdev->reset.pending, 0);
@@ -753,8 +753,8 @@ panfrost_reset(struct panfrost_device *pfdev,
for (i = 0; i < NUM_JOB_SLOTS; i++)
drm_sched_start(&pfdev->js->queue[i].sched, 0);
- /* Re-enable job interrupts now that everything has been restarted. */
- panfrost_jm_enable_interrupts(pfdev);
+ /* Re-enable interrupts now that everything has been restarted. */
+ panfrost_device_enable_int(pfdev);
dma_fence_end_signalling(cookie);
}
@@ -908,7 +908,6 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
}
panfrost_jm_reset_interrupts(pfdev);
- panfrost_jm_enable_interrupts(pfdev);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 5c393ed6e310..7fd89ee4ef9e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -340,7 +340,7 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
{
struct panfrost_mmu *mmu, *mmu_tmp;
- clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+ mmu_write(pfdev, MMU_INT_MASK, 0);
spin_lock(&pfdev->as_lock);
@@ -356,7 +356,6 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
spin_unlock(&pfdev->as_lock);
mmu_write(pfdev, MMU_INT_CLEAR, ~0);
- mmu_write(pfdev, MMU_INT_MASK, ~0);
}
static size_t get_pgsize(u64 addr, size_t size, size_t *count)
@@ -981,6 +980,12 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev)
mmu_write(pfdev, MMU_INT_MASK, 0);
}
+void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+ mmu_write(pfdev, MMU_INT_MASK, ~0);
+}
+
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev)
{
set_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
index 27c3c65ed074..689cf95caa21 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
@@ -15,6 +15,8 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping);
int panfrost_mmu_init(struct panfrost_device *pfdev);
void panfrost_mmu_fini(struct panfrost_device *pfdev);
void panfrost_mmu_reset(struct panfrost_device *pfdev);
+
+void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev);
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);
int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 07/16] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-08-26 21:18 ` [PATCH v6 07/16] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
@ 2026-08-26 21:34 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:34 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Masking GPU interrupts during device teardown creates an interrupt storm deadlock if jobs are actively running.
Pre-existing issues:
- [High] Race condition during device teardown where a pending reset work item executes concurrently with or after scheduler destruction, causing a Use-After-Free and erroneously re-enabling interrupts.
--
commit a45fd41d29f7c5d2faeeffea961ca1caad97ff77
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Split subsystem init/reset from interrupt enablement
Because MMU interrupts are only enabled when the device is reset, it
happened that after DRM device registration, the very first job targeting
the tiler heap BO would always time out. The reason is the reset sequence
is only part of PM runtime resume, which is not called explicitly at driver
probe time, and an actual reset work item manually triggered after a HW
error.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 9549ddb7854a3..0cc277efb5850 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -341,6 +365,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
> pm_runtime_get_sync(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
>
> + panfrost_device_disable_hw(pfdev);
> +
[Severity: High]
Does calling panfrost_device_disable_hw() unconditionally on driver unload
create an interrupt storm risk?
Since jobs are not guaranteed to be stopped yet, the GPU may generate a fault
or completion interrupt in panfrost_gpu_suspend_irq() right after setting
the software flag but before masking the hardware interrupt:
drivers/gpu/drm/panfrost/panfrost_gpu.c:panfrost_gpu_suspend_irq() {
set_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
<-- interrupt fires here
gpu_write(pfdev, GPU_INT_MASK, 0);
}
The panfrost_gpu_irq_handler() sees is_suspended set and returns IRQ_NONE
without clearing the hardware interrupt. Because the IRQ is level-triggered,
it would re-fire immediately upon exit, leading to a soft lockup and forcing
the kernel to permanently disable the shared IRQ line.
> panfrost_gem_fini(pfdev);
> panfrost_perfcnt_fini(pfdev);
> panfrost_jm_fini(pfdev);
[ ... ]
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index 5016d2d53da26..087905cd347a5 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
[ ... ]
> @@ -753,8 +753,8 @@ panfrost_reset(struct panfrost_device *pfdev,
> for (i = 0; i < NUM_JOB_SLOTS; i++)
> drm_sched_start(&pfdev->js->queue[i].sched, 0);
>
> - /* Re-enable job interrupts now that everything has been restarted. */
> - panfrost_jm_enable_interrupts(pfdev);
> + /* Re-enable interrupts now that everything has been restarted. */
> + panfrost_device_enable_int(pfdev);
[Severity: High]
This is a pre-existing issue, but does adding panfrost_device_enable_int()
here exacerbate a teardown race with the reset workqueue?
During teardown, panfrost_device_fini() disables hardware and then calls
panfrost_jm_fini(), which destroys the schedulers before synchronizing the
reset workqueue:
drivers/gpu/drm/panfrost/panfrost_job.c:panfrost_jm_fini() {
for (j = 0; j < NUM_JOB_SLOTS; j++) {
drm_sched_fini(&js->queue[j].sched);
}
cancel_work_sync(&pfdev->reset.work);
}
If a job timeout occurs before or during panfrost_device_fini(), the
concurrent reset work item can execute after scheduler destruction, causing
a use-after-free.
Furthermore, because this patch adds panfrost_device_enable_int() to the end
of the reset sequence, the racing reset work could erroneously clear the
is_suspended bits and re-enable hardware interrupts right before the clocks
are turned off in teardown.
>
> dma_fence_end_signalling(cookie);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=7
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 08/16] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (6 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 07/16] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:31 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 09/16] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
` (7 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
During device probe(), failure to do a PM get() will leave the usage_count
set to 0, which is the value assigned at device creation time. That means
when the autosuspend delay expires, runtime suspend callback won't be
invoked, so the device will remain powered on forever.
On top of that, failure to call PM put() during device unplug means
Panfrost device's PM usage_count increases monotonically for every new
module reload.
The combined outcome of both of the above was that devfreq OPP transition
notifications would be printed all the time, even when no jobs are being
submitted. This quickly fills the kernel ring buffer with junk.
To fix the above:
- Manually adjust the PM refcnt at device probe and removal time.
- Ensure pm_runtime_dont_use_autosuspend is called in the wind-down path.
- Call pm_runtime_put_autosuspend() when device is ready to accept jobs
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
---
drivers/gpu/drm/panfrost/panfrost_device.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 0cc277efb585..f6066aef4766 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -323,6 +323,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
pm_runtime_set_active(pfdev->base.dev);
pm_runtime_mark_last_busy(pfdev->base.dev);
pm_runtime_enable(pfdev->base.dev);
+ pm_runtime_get_noresume(pfdev->base.dev);
pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
pm_runtime_use_autosuspend(pfdev->base.dev);
@@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err < 0)
goto out_devreg;
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+
return 0;
out_devreg:
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
panfrost_device_disable_hw(pfdev);
panfrost_gem_fini(pfdev);
@@ -363,6 +368,8 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
panfrost_device_disable_hw(pfdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 08/16] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-08-26 21:18 ` [PATCH v6 08/16] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-08-26 21:31 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:31 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Calling pm_runtime_put_noidle() before pm_runtime_disable() during device teardown introduces a race condition that can lead to accessing suspended hardware.
Pre-existing issues:
- [High] Unchecked pm_runtime_get_sync() failure leads to register access while hardware is suspended.
--
commit d10f29fd6bb7082083a7547ad09e4497f2af934a
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
This commit addresses issues where the PM usage count is not correctly
adjusted during probe and unplug, which prevented the runtime suspend
callback from being invoked or caused the usage count to monotonically
increase on reloads. It manually adjusts the PM reference count in these
paths and disables autosuspend during device teardown.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 0cc277efb5850..f6066aef47666 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> if (err < 0)
> goto out_devreg;
>
> + pm_runtime_put_autosuspend(pfdev->base.dev);
> +
> return 0;
>
> out_devreg:
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
[Severity: High]
Does calling pm_runtime_put_noidle() before pm_runtime_disable() create a
race condition here?
If a concurrent job completes in the background (since schedulers have not
been stopped yet), could its interrupt handler call
pm_runtime_put_autosuspend()?
Because the teardown thread just dropped its reference, this concurrent put
could drop the usage count to 0 and suspend the device immediately.
When pm_runtime_disable() then flushes pending work and returns, the teardown
thread will proceed to panfrost_device_disable_hw(). Could accessing the
registers of the now-suspended device result in a synchronous external abort?
Would it be safer to call pm_runtime_put_noidle() after pm_runtime_disable()
to guarantee no further runtime PM state transitions occur during teardown?
> panfrost_device_disable_hw(pfdev);
> panfrost_gem_fini(pfdev);
[ ... ]
> @@ -363,6 +368,8 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> pm_runtime_get_sync(pfdev->base.dev);
[Severity: High]
This isn't a bug introduced by this patch, but does ignoring the return value
of pm_runtime_get_sync() risk causing a system crash?
If waking the device fails (for example, due to a clock or regulator
failure), it will return a negative error code and the device remains
suspended. Since the error is ignored, panfrost_device_fini() proceeds
unconditionally.
Could this lead to panfrost_device_disable_hw() attempting to access
registers while the hardware is powered off, resulting in a bus error?
Note that while follow-up patch 9 in this series adds a drm_WARN_ON() here,
it doesn't appear to abort the teardown or prevent subsequent hardware accesses
on failure.
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
[Severity: High]
Does the same race condition mentioned above also apply here in
panfrost_device_fini()?
If a background job completes and triggers a suspend after the reference is
dropped but before pm_runtime_disable() finishes, could this cause
panfrost_device_disable_hw() to access a powered-off device?
>
> panfrost_device_disable_hw(pfdev);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=8
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 09/16] drm/panfrost: Add warning messages to fatal error conditions
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (7 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 08/16] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:27 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 10/16] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
` (6 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Rather than just failing silently, let's warn the user of device remove not
being able to take an PM reference or the PM suspend path still reporting
inflight jobs. Neither situation should ever happen.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index f6066aef4766..ee4c468b882d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -9,6 +9,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -367,7 +368,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
- pm_runtime_get_sync(pfdev->base.dev);
+ drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
pm_runtime_dont_use_autosuspend(pfdev->base.dev);
pm_runtime_put_noidle(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
@@ -516,7 +517,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
{
struct panfrost_device *pfdev = dev_get_drvdata(dev);
- if (!panfrost_jm_is_idle(pfdev))
+ if (drm_WARN_ON(&pfdev->base, !panfrost_jm_is_idle(pfdev)))
return -EBUSY;
panfrost_device_disable_hw(pfdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 09/16] drm/panfrost: Add warning messages to fatal error conditions
2026-08-26 21:18 ` [PATCH v6 09/16] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
@ 2026-08-26 21:27 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:27 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Unconditional hardware register access after pm_runtime_get_sync() fails causes a bus fault.
--
commit 21c994e3f1ed58edbfb0311aa1649d2c75eb0938
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Add warning messages to fatal error conditions
This commit adds warning messages to device removal if it fails to take a PM
reference, and to the PM suspend path if it still reports inflight jobs.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index f6066aef47666..ee4c468b882d4 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -367,7 +368,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> - pm_runtime_get_sync(pfdev->base.dev);
> + drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
[Severity: High]
This is a pre-existing issue, but if pm_runtime_get_sync() fails (for example,
due to a regulator or clock enable failure during runtime resume), the device
remains unclocked and unpowered.
If execution continues unconditionally, subsequent teardown sequences like
panfrost_device_disable_hw(pfdev) and panfrost_perfcnt_fini(pfdev) will
attempt to write to MMIO registers (such as GPU_PERFCNT_CFG). On ARM platforms,
writing to MMIO registers of an unpowered or unclocked device typically triggers
a synchronous external abort and a kernel panic.
Would it be safer to handle the resource acquisition failure by skipping the
hardware-accessing teardown steps when the power on fails?
> pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_put_noidle(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=9
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 10/16] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (8 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 09/16] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:34 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 11/16] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
` (5 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
This will be of great help when testing potential races between the GPU
reset sequence and other parts of the code accessing HW registers.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 38 ++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index ee4c468b882d..0c8f53905da8 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -2,6 +2,7 @@
/* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
+#include <linux/debugfs.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
@@ -599,9 +600,46 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
};
#ifdef CONFIG_DEBUG_FS
+static int reset_get(void *data, u64 *val)
+{
+ struct panfrost_device *pfdev =
+ container_of(data, struct panfrost_device, base);
+
+ *val = atomic_read(&pfdev->reset.pending);
+ return 0;
+}
+
+static int reset_set(void *data, u64 val)
+{
+ struct panfrost_device *pfdev =
+ container_of(data, struct panfrost_device, base);
+ int ret;
+
+ ret = pm_runtime_get_if_in_use(pfdev->base.dev);
+
+ if (ret > 0) {
+ panfrost_device_schedule_reset(pfdev);
+ flush_work(&pfdev->reset.work);
+ pm_runtime_put(pfdev->base.dev);
+ }
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops,
+ reset_get, reset_set,
+ "0x%08llx\n");
+
+static void panfrost_reset_debugfs_init(struct drm_minor *minor)
+{
+ debugfs_create_file("reset", 0600, minor->debugfs_root,
+ minor->dev, &panfrost_reset_debugfs_fops);
+}
+
void panfrost_device_debugfs_init(struct drm_minor *minor)
{
panfrost_gems_debugfs_init(minor);
panfrost_sched_debugfs_init(minor);
+ panfrost_reset_debugfs_init(minor);
}
#endif // CONFIG_DEBUG_FS
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 10/16] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-08-26 21:18 ` [PATCH v6 10/16] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-08-26 21:34 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:34 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The debugfs reset knob silently fails when CONFIG_PM is disabled or when runtime PM is disabled for the device.
--
commit 306a309307e2e6679d0d0021abfa0cde0b693759
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Add debugfs knob for manually triggering a GPU reset
This commit introduces a new debugfs interface to manually schedule a GPU
reset. This allows developers to test potential races between the reset
sequence and other hardware register accesses.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -599,9 +600,46 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
> };
>
> #ifdef CONFIG_DEBUG_FS
> +static int reset_get(void *data, u64 *val)
> +{
> + struct panfrost_device *pfdev =
> + container_of(data, struct panfrost_device, base);
> +
> + *val = atomic_read(&pfdev->reset.pending);
> + return 0;
> +}
> +
> +static int reset_set(void *data, u64 val)
> +{
> + struct panfrost_device *pfdev =
> + container_of(data, struct panfrost_device, base);
> + int ret;
> +
> + ret = pm_runtime_get_if_in_use(pfdev->base.dev);
> +
> + if (ret > 0) {
[Severity: Medium]
Does this strict condition handle configurations where power management is
disabled?
If the kernel is built without CONFIG_PM, or if runtime PM is disabled for
the device, pm_runtime_get_if_in_use() returns -EINVAL. Because the check
requires the return value to be strictly greater than 0, it will silently
skip the block.
Could this cause the debugfs reset knob to fail silently on these
configurations, making the testing capability unavailable without any
indication to the user?
> + panfrost_device_schedule_reset(pfdev);
> + flush_work(&pfdev->reset.work);
> + pm_runtime_put(pfdev->base.dev);
> + }
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=10
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 11/16] drm/panfrost: Move perfcnt GPU disable sequence into a helper
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (9 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 10/16] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 12/16] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
` (4 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Just for the sake of avoiding repetition.
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 31 ++++++++++++-----------------
1 file changed, 13 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index 7020c0192e18..f71534e741b6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -35,6 +35,16 @@ struct panfrost_perfcnt {
struct completion dump_comp;
};
+static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
+{
+ gpu_write(pfdev, GPU_PERFCNT_CFG,
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+}
+
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
{
complete(&pfdev->perfcnt->dump_comp);
@@ -193,12 +203,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
if (user != perfcnt->user)
return -EINVAL;
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
+ panfrost_perfcnt_hw_disable(pfdev);
perfcnt->user = NULL;
drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
@@ -327,12 +332,7 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
perfcnt->bosize = size;
/* Start with everything disabled. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ panfrost_perfcnt_hw_disable(pfdev);
init_completion(&perfcnt->dump_comp);
mutex_init(&perfcnt->lock);
@@ -344,10 +344,5 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
{
/* Disable everything before leaving. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ panfrost_perfcnt_hw_disable(pfdev);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 12/16] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (10 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 11/16] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 13/16] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
` (3 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The GPU cache flush/invalidate operation is unnecessary, because at present
all counters are always enabled for all sessions, and there are no plans to
expand the uAPI so as to let UM select specific counter groups.
If the above was ever implemented, then different counter groups being
selected between sessions might leave stale data in the GPU caches, so
the flush/invalidate would have to be reintroduced.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index f71534e741b6..ffc77121070e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -124,21 +124,10 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
/*
- * Invalidate the cache and clear the counters to start from a fresh
- * state.
+ * Clear the counters to start from a fresh state.
*/
- reinit_completion(&pfdev->perfcnt->dump_comp);
- gpu_write(pfdev, GPU_INT_CLEAR,
- GPU_IRQ_CLEAN_CACHES_COMPLETED |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_INV_CACHES);
- ret = wait_for_completion_timeout(&pfdev->perfcnt->dump_comp,
- msecs_to_jiffies(1000));
- if (!ret) {
- ret = -ETIMEDOUT;
- goto err_vunmap;
- }
ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
if (ret < 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 13/16] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (11 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 12/16] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:36 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 14/16] drm/panfrost: Introduce a reset lock Adrián Larumbe
` (2 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
If the GPU and CPU are fully coherent, then there's no need to flush the
GPU caches after a perfcnt sample is made available.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index ffc77121070e..b3f71d7fd82a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
{
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ if (pfdev->features.selected_coherency != COHERENCY_ACE)
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ else
+ complete(&pfdev->perfcnt->dump_comp);
}
static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 13/16] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-26 21:18 ` [PATCH v6 13/16] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-08-26 21:36 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:36 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing cancellation and synchronization of asynchronous GPU tasks after a signal or timeout leads to software race conditions, hardware state corruption, and GPU page faults.
--
commit 5c3d775056a5b2e3ebdba73f1d3b9858fe3716ba
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
This commit avoids flushing the GPU caches after a perfcnt sample is made
available if the GPU and CPU are fully coherent.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> index ffc77121070e2..b3f71d7fd82a9 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> @@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
>
> void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
> {
> - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
> + if (pfdev->features.selected_coherency != COHERENCY_ACE)
> + gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
> + else
> + complete(&pfdev->perfcnt->dump_comp);
> }
>
> static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
[Severity: High]
This is a pre-existing issue, but does this interruptible wait in
panfrost_perfcnt_dump_locked() leave the GPU hardware in a busy state if it
aborts early?
When an unprivileged user issues the DRM_IOCTL_PANFROST_PERFCNT_DUMP ioctl,
the kernel waits for the sample to complete:
panfrost_perfcnt_dump_locked() {
...
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
...
}
If the user process is interrupted by a signal, this wait returns early,
leaving the GPU_CMD_PERFCNT_SAMPLE command executing asynchronously on the
hardware.
If the syscall is restarted by the kernel, the driver re-issues the
GPU_CMD_PERFCNT_SAMPLE command:
panfrost_perfcnt_dump_locked() {
...
reinit_completion(&pfdev->perfcnt->dump_comp);
...
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
...
}
This issues a new command while the previous one is still in-flight, which
can corrupt the hardware state machine.
Alternatively, if the process exits instead,
panfrost_perfcnt_disable_locked() unmaps the GPU memory without waiting for
the in-flight sample to finish:
panfrost_perfcnt_disable_locked() {
...
drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
perfcnt->buf = NULL;
panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
...
}
When the GPU finally writes the sample data, it hits unmapped memory,
causing a GPU page fault and forcing a GPU hard reset. Could proper
cancellation and synchronization of these asynchronous GPU tasks be added
after a signal or timeout?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=13
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 14/16] drm/panfrost: Introduce a reset lock
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (12 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 13/16] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 15/16] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
2026-08-26 21:18 ` [PATCH v6 16/16] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field Adrián Larumbe
15 siblings, 0 replies; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
So as to avoid accessing HW register while a reset is ongoing, a read/write
semaphore that envelopes the reset sequence will help driver entry points
avoid racing with it. For now, the only such racy entry point is the ioctl
that returns the current GPU timestmap.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 2 ++
drivers/gpu/drm/panfrost/panfrost_device.h | 1 +
drivers/gpu/drm/panfrost/panfrost_drv.c | 9 ++++++---
drivers/gpu/drm/panfrost/panfrost_job.c | 1 +
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 0c8f53905da8..ce1d3eb40332 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -491,6 +491,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
void panfrost_device_reset(struct panfrost_device *pfdev)
{
+ guard(rwsem_write)(&pfdev->reset.lock);
+
panfrost_gpu_soft_reset(pfdev);
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index c94546b49662..0570cb1d650d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -165,6 +165,7 @@ struct panfrost_device {
struct {
struct workqueue_struct *wq;
struct work_struct work;
+ struct rw_semaphore lock;
atomic_t pending;
} reset;
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index f77780c72a1a..571a26b84126 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -41,9 +41,12 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
if (ret)
return ret;
- panfrost_cycle_counter_get(pfdev);
- *arg = panfrost_timestamp_read(pfdev);
- panfrost_cycle_counter_put(pfdev);
+ /* We should not read timestamp register while the GPU is being reset */
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_cycle_counter_get(pfdev);
+ *arg = panfrost_timestamp_read(pfdev);
+ panfrost_cycle_counter_put(pfdev);
+ }
pm_runtime_put(pfdev->base.dev);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 087905cd347a..6107485a5332 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -876,6 +876,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
mutex_init(&pfdev->sched_lock);
+ init_rwsem(&pfdev->reset.lock);
js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
if (js->irq < 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* [PATCH v6 15/16] drm/panfrost: Fix races between perfcnt and reset sequence
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (13 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 14/16] drm/panfrost: Introduce a reset lock Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:35 ` sashiko-bot
2026-08-26 21:18 ` [PATCH v6 16/16] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field Adrián Larumbe
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Formerly, the reset sequence would race with panfrost_mmu_as_put()
when tearing down a perfcnt session. On top of that, poking GPU
registers to program a perfcnt session or obtaining a dump might lead to
undefined behaviour when done at the same time a reset was ongoing.
Use the reset r/w semaphore to govern access to the hardware at reset
time. On top of that, expand the DRM uAPI for the perfcnt DUMP operation
so that userspace can be made aware of a reset having happened, because
that means counters will go back to 0 and can no longer be accumulated
to values previously kept in user space.
The new perfcnt-aware reset sequence also takes care to reestablish
perfcnt to its original configuration if there was an enabled session.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 1 +
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 177 +++++++++++++++++++---------
drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
include/uapi/drm/panfrost_drm.h | 3 +-
4 files changed, 127 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index ce1d3eb40332..eeab1c3525b0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -497,6 +497,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev)
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
panfrost_jm_reset_interrupts(pfdev);
+ panfrost_perfcnt_reset(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index b3f71d7fd82a..7dcf27add7ae 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -11,6 +11,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_features.h"
@@ -25,14 +26,18 @@
#define BYTES_PER_COUNTER 4
#define BLOCKS_PER_COREGROUP 8
#define V4_SHADERS_PER_COREGROUP 4
+#define PERFCNT_DUMP_MAX_RETRIES 5
struct panfrost_perfcnt {
struct panfrost_gem_mapping *mapping;
+ unsigned int counterset;
size_t bosize;
void *buf;
struct panfrost_file_priv *user;
struct mutex lock;
struct completion dump_comp;
+ bool reset_happened;
+ bool reset_failed;
};
static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
@@ -58,25 +63,93 @@ void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
complete(&pfdev->perfcnt->dump_comp);
}
-static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
+static int panfrost_perfcnt_hw_enable(struct panfrost_device *pfdev)
{
- u64 gpuva;
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u32 cfg, as;
+ int ret;
+
+ ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ if (ret < 0)
+ return ret;
+
+ as = ret;
+ cfg = GPU_PERFCNT_CFG_AS(as) |
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
+
+ /*
+ * Bifrost GPUs have 2 set of counters, but we're only interested by
+ * the first one for now.
+ */
+ if (panfrost_model_is_bifrost(pfdev))
+ cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
+
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
+
+ /*
+ * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
+ * counters.
+ */
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ else
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
+
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ return 0;
+}
+
+static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
+ u64 *reset_happened)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
+ s64 retries = PERFCNT_DUMP_MAX_RETRIES;
int ret;
- reinit_completion(&pfdev->perfcnt->dump_comp);
- gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT;
- gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
- gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
- gpu_write(pfdev, GPU_INT_CLEAR,
- GPU_IRQ_CLEAN_CACHES_COMPLETED |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
+dump_retry:
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *reset_happened = perfcnt->reset_happened;
+ perfcnt->reset_happened = false;
+ if (perfcnt->reset_failed) {
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ return ret;
+ perfcnt->reset_failed = false;
+ }
+
+ reinit_completion(&pfdev->perfcnt->dump_comp);
+
+ gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
+ gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
+ GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
+ }
+
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
- if (!ret)
- ret = -ETIMEDOUT;
- else if (ret > 0)
- ret = 0;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ if (ret > 0) {
+ if (perfcnt->reset_happened) {
+ if (--retries >= 0)
+ goto dump_retry;
+ else
+ ret = -EBUSY;
+ } else {
+ ret = 0;
+ }
+ } else if (!ret) {
+ ret = -ETIMEDOUT;
+ }
+ }
return ret;
}
@@ -87,9 +160,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
{
struct panfrost_file_priv *user = file_priv->driver_priv;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- struct iosys_map map;
struct drm_gem_shmem_object *bo;
- u32 cfg, as;
+ struct iosys_map map;
int ret;
if (user == perfcnt->user)
@@ -122,7 +194,9 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
ret = drm_gem_vmap(&bo->base, &map);
if (ret)
goto err_put_mapping;
+
perfcnt->buf = map.vaddr;
+ perfcnt->counterset = counterset;
panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
@@ -132,44 +206,20 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
- if (ret < 0)
- goto err_vunmap;
-
- as = ret;
- cfg = GPU_PERFCNT_CFG_AS(as) |
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
-
- /*
- * Bifrost GPUs have 2 set of counters, but we're only interested by
- * the first one for now.
- */
- if (panfrost_model_is_bifrost(pfdev))
- cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
-
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
-
- /*
- * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
- * counters.
- */
- if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
- else
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
-
- gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
-
- if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ if (perfcnt->reset_failed) {
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ goto err_vunmap;
+ }
+ perfcnt->reset_happened = false;
+ perfcnt->reset_failed = false;
+ perfcnt->user = user;
+ }
/* The BO ref is retained by the mapping. */
drm_gem_object_put(&bo->base);
- perfcnt->user = user;
-
return 0;
err_vunmap:
@@ -195,13 +245,15 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
if (user != perfcnt->user)
return -EINVAL;
- panfrost_perfcnt_hw_disable(pfdev);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_perfcnt_hw_disable(pfdev);
+ panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
+ perfcnt->user = NULL;
+ }
- perfcnt->user = NULL;
drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
perfcnt->buf = NULL;
panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
- panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
panfrost_gem_mapping_put(perfcnt->mapping);
perfcnt->mapping = NULL;
pm_runtime_put_autosuspend(pfdev->base.dev);
@@ -255,7 +307,7 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
goto out;
}
- ret = panfrost_perfcnt_dump_locked(pfdev);
+ ret = panfrost_perfcnt_dump_locked(pfdev, &req->hw_reset);
if (ret)
goto out;
@@ -338,3 +390,20 @@ void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
/* Disable everything before leaving. */
panfrost_perfcnt_hw_disable(pfdev);
}
+
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+
+ if (drm_WARN_ON(&pfdev->base, !perfcnt))
+ return;
+
+ lockdep_assert_held(&pfdev->reset.lock);
+
+ if (!perfcnt->user)
+ return;
+
+ perfcnt->reset_failed = !!panfrost_perfcnt_hw_enable(pfdev);
+ perfcnt->reset_happened = true;
+ complete(&perfcnt->dump_comp);
+}
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
index 8bbcf5f5fb33..8b9bc704b634 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
@@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev);
#endif
diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h
index 50d5337f35ef..3bbf9220103d 100644
--- a/include/uapi/drm/panfrost_drm.h
+++ b/include/uapi/drm/panfrost_drm.h
@@ -47,7 +47,7 @@ extern "C" {
* them for anything but debugging purpose.
*/
#define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable)
-#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
+#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
#define PANFROST_JD_REQ_FS (1 << 0)
#define PANFROST_JD_REQ_CYCLE_COUNT (1 << 1)
@@ -272,6 +272,7 @@ struct drm_panfrost_perfcnt_enable {
struct drm_panfrost_perfcnt_dump {
__u64 buf_ptr;
+ __u64 hw_reset;
};
/* madvise provides a way to tell the kernel in case a buffers contents
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 15/16] drm/panfrost: Fix races between perfcnt and reset sequence
2026-08-26 21:18 ` [PATCH v6 15/16] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
@ 2026-08-26 21:35 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:35 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Hardware initialization is bypassed on new perfcnt sessions, breaking performance counters.
- [High] Unconditional MMU AS reference put causes reference count underflow.
- [Medium] Mixing goto-based error handling with scoped_guard violates the kernel cleanup API.
--
commit 036d049b8b6f60dc9ba18742b07e01b404ef96c3
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix races between perfcnt and reset sequence
Formerly, the reset sequence would race with panfrost_mmu_as_put()
when tearing down a perfcnt session. On top of that, poking GPU
registers to program a perfcnt session or obtaining a dump might lead to
undefined behaviour when done at the same time a reset was ongoing.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> index b3f71d7fd82a9..7dcf27add7ae7 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
[ ... ]
> @@ -132,44 +206,20 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
>
> - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> - if (ret < 0)
> - goto err_vunmap;
> -
[ ... ]
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + if (perfcnt->reset_failed) {
> + ret = panfrost_perfcnt_hw_enable(pfdev);
> + if (ret)
> + goto err_vunmap;
> + }
[Severity: High]
Does this code inadvertently skip hardware initialization for new perfcnt
sessions?
Earlier in panfrost_perfcnt_enable_locked(), the function returns early if
perfcnt->user is not NULL. Because reset_failed is only set during a reset
while a session is already active, reset_failed is guaranteed to be false
when creating a new session.
Consequently, panfrost_perfcnt_hw_enable() is never called here, the MMU
address space is not acquired, and hardware performance counters are never
enabled.
[Severity: Medium]
Is it safe to mix goto-based error handling with scoped_guard() blocks here?
According to the kernel cleanup subsystem guidelines, the usage of "goto" and
cleanup helpers should never be mixed in the same function to prevent
confusing ownership semantics and potential resource leak bugs.
The jump to err_vunmap directly exits the scoped_guard block, breaking this
rule.
> + perfcnt->reset_happened = false;
> + perfcnt->reset_failed = false;
> + perfcnt->user = user;
> + }
>
> /* The BO ref is retained by the mapping. */
> drm_gem_object_put(&bo->base);
[ ... ]
> @@ -195,13 +245,15 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
> if (user != perfcnt->user)
> return -EINVAL;
>
> - panfrost_perfcnt_hw_disable(pfdev);
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + panfrost_perfcnt_hw_disable(pfdev);
> + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
[Severity: High]
Can this unconditional call to panfrost_mmu_as_put() cause a reference count
underflow regression?
If panfrost_perfcnt_hw_enable() was skipped during initialization as noted
above, or if it failed to re-acquire the AS during reset recovery, the perfcnt
session does not actually hold an MMU AS reference.
When a userspace client closes the perfcnt session, panfrost_mmu_as_put() is
called unconditionally, which will drive the mmu->as_count reference count to
-1 and trigger a kernel warning.
> + perfcnt->user = NULL;
> + }
>
> - perfcnt->user = NULL;
> drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
> perfcnt->buf = NULL;
> panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
> - panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
> panfrost_gem_mapping_put(perfcnt->mapping);
> perfcnt->mapping = NULL;
> pm_runtime_put_autosuspend(pfdev->base.dev);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=15
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v6 16/16] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field
2026-08-26 21:18 [PATCH v6 00/16] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (14 preceding siblings ...)
2026-08-26 21:18 ` [PATCH v6 15/16] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
@ 2026-08-26 21:18 ` Adrián Larumbe
2026-08-26 21:37 ` sashiko-bot
15 siblings, 1 reply; 25+ messages in thread
From: Adrián Larumbe @ 2026-08-26 21:18 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Expose expanded PERFCNT_DUMP IOCTL interface to user mode.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_drv.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 571a26b84126..7869a0cfc4c3 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -808,6 +808,7 @@ static const struct file_operations panfrost_drm_driver_fops = {
* - 1.6 - adds PANFROST_BO_MAP_WB, PANFROST_IOCTL_SYNC_BO,
* PANFROST_IOCTL_QUERY_BO_INFO and
* DRM_PANFROST_PARAM_SELECTED_COHERENCY
+ * - 1.7 - adds PERFCNT_DUMP req hw_reset field
*/
static const struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
@@ -820,7 +821,7 @@ static const struct drm_driver panfrost_drm_driver = {
.name = "panfrost",
.desc = "panfrost DRM",
.major = 1,
- .minor = 6,
+ .minor = 7,
.gem_create_object = panfrost_gem_create_object,
.gem_prime_import = panfrost_gem_prime_import,
--
2.55.0
^ permalink raw reply related [flat|nested] 25+ messages in thread* Re: [PATCH v6 16/16] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field
2026-08-26 21:18 ` [PATCH v6 16/16] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field Adrián Larumbe
@ 2026-08-26 21:37 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-08-26 21:37 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware performance counter initialization is skipped on new sessions due to an incorrect condition, leading to an MMU address space reference count underflow and resource leak.
--
commit 036d049b8b6f60dc9ba18742b07e01b404ef96c3
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix races between perfcnt and reset sequence
Formerly, the reset sequence would race with panfrost_mmu_as_put()
when tearing down a perfcnt session. This commit introduces a read/write
semaphore to govern access to the hardware at reset time, and expands the
DRM uAPI for the perfcnt DUMP operation.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> index b3f71d7fd82a9..7dcf27add7ae7 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
[ ... ]
> @@ -132,44 +206,20 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
>
> - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> - if (ret < 0)
> - goto err_vunmap;
> -
[ ... ]
> - gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
> -
> - if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + if (perfcnt->reset_failed) {
> + ret = panfrost_perfcnt_hw_enable(pfdev);
> + if (ret)
> + goto err_vunmap;
> + }
[Severity: High]
Does this skip the initial hardware enablement on new sessions? Since
perfcnt->reset_failed would be false initially, it seems
panfrost_perfcnt_hw_enable() is bypassed entirely, which also means we miss
taking the MMU AS reference via panfrost_mmu_as_get().
> + perfcnt->reset_happened = false;
> + perfcnt->reset_failed = false;
> + perfcnt->user = user;
> + }
>
> /* The BO ref is retained by the mapping. */
> drm_gem_object_put(&bo->base);
[ ... ]
> @@ -195,13 +245,15 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
> if (user != perfcnt->user)
> return -EINVAL;
>
> - panfrost_perfcnt_hw_disable(pfdev);
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + panfrost_perfcnt_hw_disable(pfdev);
> + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
[Severity: High]
If panfrost_perfcnt_hw_enable() was skipped in
panfrost_perfcnt_enable_locked(), will this unconditionally drop a reference
that was never acquired? It looks like this could underflow the AS
reference count, which would prevent subsequent allocations since a negative
reference count isn't considered free.
> + perfcnt->user = NULL;
> + }
>
> - perfcnt->user = NULL;
> drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-claude-fixes-v6-0-78119af409e7@collabora.com?part=16
^ permalink raw reply [flat|nested] 25+ messages in thread