* [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-08 16:46 ` kernel test robot
2026-10-07 23:42 ` [PATCH v13 02/15] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
` (13 subsequent siblings)
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
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_gem.c | 26 ++++++++++++++----------
drivers/gpu/drm/panfrost/panfrost_gem.h | 7 ++++---
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 8 ++------
5 files changed, 28 insertions(+), 27 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_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3a7fce428898..f39c0f50ed4f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -15,20 +15,24 @@
#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) {
+ int 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);
+ }
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
- !panfrost_transparent_hugepage)
- return;
+ return panfrost_gem_shrinker_init(pfdev);
+}
- 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);
+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] 22+ messages in thread* Re: [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down
2026-10-07 23:42 ` [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-10-08 16:46 ` kernel test robot
0 siblings, 0 replies; 22+ messages in thread
From: kernel test robot @ 2026-10-08 16:46 UTC (permalink / raw)
To: Adrián Larumbe, 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, Robin Murphy, Philipp Zabel
Cc: llvm, oe-kbuild-all, dri-devel, linux-kernel,
Collabora Kernel Team, Adrián Larumbe, Neil Armstrong
Hi Adrián,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 3ab04dcce43cd4fdaed94f08f4e6289be4921ea4]
url: https://github.com/intel-lab-lkp/linux/commits/Adri-n-Larumbe/drm-panfrost-Move-shrinker-initialization-and-unplug-one-level-down/20261008-004244
base: 3ab04dcce43cd4fdaed94f08f4e6289be4921ea4
patch link: https://lore.kernel.org/r/20261008-claude-fixes-v13-1-d6a493185860%40collabora.com
patch subject: [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down
config: arm64-randconfig-1002-20261008 (https://download.01.org/0day-ci/archive/20261009/202610090034.XP8OKWaV-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261009/202610090034.XP8OKWaV-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202610090034.XP8OKWaV-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:98 function parameter 'pfdev' not described in 'panfrost_gem_shrinker_init'
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:98 Excess function parameter 'dev' description in 'panfrost_gem_shrinker_init' (did you mean one of: 'pfdev')
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:119 function parameter 'pfdev' not described in 'panfrost_gem_shrinker_fini'
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:119 expecting prototype for panfrost_gem_shrinker_cleanup(). Prototype was for panfrost_gem_shrinker_fini() instead
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:98 function parameter 'pfdev' not described in 'panfrost_gem_shrinker_init'
>> Warning: drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c:98 Excess function parameter 'dev' description in 'panfrost_gem_shrinker_init' (did you mean one of: 'pfdev')
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v13 02/15] drm/panfrost: Move lock and modparam initialisations into their subsystems
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 03/15] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
` (12 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Then we can also discard panfrost_drv.h since the transparent_huge_page
doesn't need to be accessed elsewhere. We can also simplify the
creation of a mount point, because drm_gem_get_huge_mnt() would never
return NULL if drm_gem_huge_mnt_create() returns 0 when kernel is built
with support for transparent huge pages.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 7 -------
drivers/gpu/drm/panfrost/panfrost_drv.c | 10 ----------
drivers/gpu/drm/panfrost/panfrost_drv.h | 9 ---------
drivers/gpu/drm/panfrost/panfrost_gem.c | 19 +++++++++++++------
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, 22 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 05c40d5a20b5..c1a2e93a2b9b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -216,13 +216,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
{
int err;
- 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 9882a3ede75f..59af79a534fa 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;
@@ -979,9 +972,6 @@ static int panfrost_probe(struct platform_device *pdev)
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)
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
deleted file mode 100644
index a2277ec61aab..000000000000
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ /dev/null
@@ -1,9 +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__
-
-extern bool panfrost_transparent_hugepage;
-
-#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index f39c0f50ed4f..383ac8cd5645 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -11,20 +11,27 @@
#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
+static 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)
{
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && panfrost_transparent_hugepage) {
+ if (panfrost_transparent_hugepage) {
int err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
- if (drm_gem_get_huge_mnt(&pfdev->base))
+ if (err)
+ drm_warn(&pfdev->base,
+ "Can't use Transparent Hugepage (%d)\n", err);
+ else
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);
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 d59b4863b8ad..476c936b54e6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -874,6 +874,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] 22+ messages in thread* [PATCH v13 03/15] drm/panfrost: Move debugfs initialisation to relevant subsystems
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 01/15] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 02/15] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 04/15] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
` (11 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
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 c1a2e93a2b9b..5bf528f41278 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -561,3 +561,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_gem_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 59af79a534fa..55fc22e8d4d4 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 383ac8cd5645..518f24549b9d 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"
@@ -735,8 +736,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;
@@ -756,4 +757,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_gem_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..3008afcf4bff 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_gem_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 476c936b54e6..00fb36c577df 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>
@@ -1138,3 +1140,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] 22+ messages in thread* [PATCH v13 04/15] drm/panfrost: Skip NULL checks for clock enable/disabling
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (2 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 03/15] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 05/15] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
` (10 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
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 5bf528f41278..b3a53504bd01 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -510,11 +510,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);
@@ -524,7 +522,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))
@@ -545,9 +543,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] 22+ messages in thread* [PATCH v13 05/15] drm/panfrost: Consolidate device clock management and reset
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (3 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 04/15] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 06/15] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
` (9 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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
repetitions between driver init/fini and device resume/suspend, since
these clocks are always handled in bulk.
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 124 ++++++++++++++++-------------
1 file changed, 69 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index b3a53504bd01..50f4713bfcfd 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -34,10 +34,46 @@ static void panfrost_reset_fini(struct panfrost_device *pfdev)
reset_control_assert(pfdev->rstc);
}
-static int panfrost_clk_init(struct panfrost_device *pfdev)
+static int panfrost_clks_enable(struct panfrost_device *pfdev, bool on_resume)
{
int err;
+
+ err = clk_prepare_enable(pfdev->clock);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(pfdev->bus_clock);
+ if (err)
+ goto disable_clock;
+
+ if (on_resume) {
+ 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;
+}
+
+static void panfrost_clks_disable(struct panfrost_device *pfdev, bool on_suspend)
+{
+ if (on_suspend)
+ clk_disable_unprepare(pfdev->bus_ace_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
+}
+
+static int panfrost_clk_init(struct panfrost_device *pfdev)
+{
unsigned long rate;
+ int err;
pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
if (IS_ERR(pfdev->clock)) {
@@ -48,53 +84,59 @@ 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);
+ err = clk_prepare(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);
+ goto unprepare_clock;
}
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);
+ err = clk_prepare(pfdev->bus_clock);
if (err)
- goto disable_clock;
+ goto unprepare_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;
+ goto unprepare_bus_clock;
}
- err = clk_prepare_enable(pfdev->bus_ace_clock);
+ err = clk_prepare(pfdev->bus_ace_clock);
+ if (err)
+ goto unprepare_bus_clock;
+
+ err = panfrost_clks_enable(pfdev, true);
if (err)
- goto disable_bus_clock;
+ goto unprepare_bus_ace_clock;
return 0;
-disable_bus_clock:
- clk_disable_unprepare(pfdev->bus_clock);
-disable_clock:
- clk_disable_unprepare(pfdev->clock);
+unprepare_bus_ace_clock:
+ clk_unprepare(pfdev->bus_ace_clock);
+unprepare_bus_clock:
+ clk_unprepare(pfdev->bus_clock);
+unprepare_clock:
+ clk_unprepare(pfdev->clock);
return err;
}
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_clks_disable(pfdev, true);
+ clk_unprepare(pfdev->bus_ace_clock);
+ clk_unprepare(pfdev->bus_clock);
+ clk_unprepare(pfdev->clock);
}
static int panfrost_regulator_init(struct panfrost_device *pfdev)
@@ -436,34 +478,17 @@ static int panfrost_device_runtime_resume(struct device *dev)
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;
+ ret = panfrost_clks_enable(pfdev, true);
+ if (ret) {
+ reset_control_assert(pfdev->rstc);
+ return ret;
+ }
}
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)
@@ -480,9 +505,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
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);
+ panfrost_clks_disable(pfdev, true);
reset_control_assert(pfdev->rstc);
}
@@ -506,13 +529,9 @@ static int panfrost_device_resume(struct device *dev)
}
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- ret = clk_enable(pfdev->clock);
+ ret = panfrost_clks_enable(pfdev, false);
if (ret)
goto err_clk;
-
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
}
ret = pm_runtime_force_resume(dev);
@@ -523,10 +542,7 @@ 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);
-err_bus_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->clock);
+ panfrost_clks_disable(pfdev, false);
err_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
@@ -542,10 +558,8 @@ static int panfrost_device_suspend(struct device *dev)
if (ret)
return ret;
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
- }
+ if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
+ panfrost_clks_disable(pfdev, false);
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v13 06/15] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (4 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 05/15] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:55 ` sashiko-bot
2026-10-07 23:42 ` [PATCH v13 07/15] drm/panfrost: Explicitly enable MMU interrupts at device init Adrián Larumbe
` (8 subsequent siblings)
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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 outcome of both of the above meant 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.
- Because MMU interrupts are only enabled when the device is reset,
the very first job targeting the tiler heap BO after device probe()
would always time out, since the driver's PM runtime resume callback
would not be invoked.
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
Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_drv.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 55fc22e8d4d4..907d4a14a0b5 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -852,6 +852,11 @@ static int panfrost_probe(struct platform_device *pdev)
goto err_out0;
}
+ /* The reason we must manually set the PM status and usage counter is
+ * we have just powered the device up but did not go through the PM
+ * runtime resume callback, so we need to update these ourselves.
+ */
+ pm_runtime_get_noresume(pfdev->base.dev);
pm_runtime_set_active(pfdev->base.dev);
pm_runtime_mark_last_busy(pfdev->base.dev);
pm_runtime_enable(pfdev->base.dev);
@@ -866,13 +871,16 @@ static int panfrost_probe(struct platform_device *pdev)
if (err < 0)
goto err_out1;
+ pm_runtime_put_autosuspend(pfdev->base.dev);
return 0;
err_out1:
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
err_out0:
return err;
}
@@ -884,9 +892,12 @@ static void panfrost_remove(struct platform_device *pdev)
drm_dev_unregister(&pfdev->base);
pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
+
}
static ssize_t profiling_show(struct device *dev,
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v13 06/15] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-10-07 23:42 ` [PATCH v13 06/15] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-10-07 23:55 ` sashiko-bot
0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-10-07 23:55 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] drm/panfrost: missing error check on pm_runtime_get_sync() in device removal causes SError
--
commit c158c940eec9a323d868411adbbd13b59c6449b8
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
This commit fixes issues where the panfrost device PM usage count was left
at 0 after probe failure, or increased monotonically after every module
reload. It manually adjusts the PM refcnt at probe and removal, and ensures
autosuspend functions are called properly.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 55fc22e8d4d4e..907d4a14a0b55 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
[ ... ]
> @@ -884,9 +892,12 @@ static void panfrost_remove(struct platform_device *pdev)
> drm_dev_unregister(&pfdev->base);
>
> pm_runtime_get_sync(pfdev->base.dev);
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> panfrost_device_fini(pfdev);
[Severity: High]
Does ignoring the return value of pm_runtime_get_sync() in panfrost_remove()
allow the driver to write to unpowered hardware?
During device removal, if the power domain or regulators fail to resume the
device, pm_runtime_get_sync() returns a negative error code. Because the
code proceeds unconditionally to call panfrost_device_fini(), it invokes
panfrost_gpu_fini() and panfrost_perfcnt_fini(), which perform direct MMIO
register writes (such as to SHADER_PWROFF_LO and GPU_PERFCNT_CFG).
Accessing these registers while the hardware is physically powered off can
cause an Oops due to a synchronous external abort on ARM architectures.
Note that even if follow-up patches attempt to address this by wrapping the
call in drm_WARN_ON(), that does not abort or bypass the subsequent hardware
accesses. The system would still crash following the warning.
> pm_runtime_set_suspended(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
> +
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-claude-fixes-v13-0-d6a493185860@collabora.com?part=6
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v13 07/15] drm/panfrost: Explicitly enable MMU interrupts at device init
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (5 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 06/15] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 08/15] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
` (7 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Because the device must be in a position to accept jobs between the time
drm_dev_register() is called and autosuspend first kicks in, there's a very
narrow window inbetween during which jobs targeting the tiler buffer
object would time out, since the device's PM status is 'Active', but no MMU
interrupts were enabled at device initialisation time.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Fixes: 73e467f60acd ("drm/panfrost: Consolidate reset handling")
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_mmu.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 5c393ed6e310..faa62643caf1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -336,6 +336,12 @@ void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu
WARN_ON(atomic_read(&mmu->as_count) < 0);
}
+static void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ mmu_write(pfdev, MMU_INT_CLEAR, ~0);
+ mmu_write(pfdev, MMU_INT_MASK, ~0);
+}
+
void panfrost_mmu_reset(struct panfrost_device *pfdev)
{
struct panfrost_mmu *mmu, *mmu_tmp;
@@ -355,8 +361,7 @@ 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);
+ panfrost_mmu_enable_interrupts(pfdev);
}
static size_t get_pgsize(u64 addr, size_t size, size_t *count)
@@ -973,6 +978,8 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
return err;
}
+ panfrost_mmu_enable_interrupts(pfdev);
+
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v13 08/15] drm/panfrost: Move all DRM device initialisation into device_init()
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (6 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 07/15] drm/panfrost: Explicitly enable MMU interrupts at device init Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:54 ` sashiko-bot
2026-10-07 23:42 ` [PATCH v13 09/15] drm/panfrost: Warn of RPM get failure at device remove time Adrián Larumbe
` (6 subsequent siblings)
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 49 ++++++++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_drv.c | 52 +-----------------------------
2 files changed, 50 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 50f4713bfcfd..a6deabb39fd2 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"
@@ -256,8 +257,15 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
int panfrost_device_init(struct panfrost_device *pfdev)
{
+ bool device_initialised = false;
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;
+
#ifdef CONFIG_DEBUG_FS
mutex_init(&pfdev->debugfs.gems_lock);
INIT_LIST_HEAD(&pfdev->debugfs.gems_list);
@@ -319,8 +327,35 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_perfcnt;
+ device_initialised = true;
+
+ /* The reason we must manually set the PM status and usage counter is
+ * we have just powered the device up but did not go through the PM
+ * runtime resume callback, so we need to update these ourselves.
+ */
+ pm_runtime_get_noresume(pfdev->base.dev);
+ 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_disable_rpm;
+
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+
return 0;
+err_disable_rpm:
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
+ panfrost_gem_fini(pfdev);
out_perfcnt:
panfrost_perfcnt_fini(pfdev);
out_job:
@@ -339,11 +374,22 @@ int panfrost_device_init(struct panfrost_device *pfdev)
panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
+
+ if (device_initialised) {
+ pm_runtime_set_suspended(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
+ }
+
return err;
}
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_disable(pfdev->base.dev);
+
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
@@ -354,6 +400,9 @@ 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);
+ pm_runtime_put_noidle(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 907d4a14a0b5..f77780c72a1a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -830,7 +830,6 @@ static const struct drm_driver panfrost_drm_driver = {
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);
@@ -839,50 +838,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;
-
- err = panfrost_device_init(pfdev);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Fatal error during GPU init\n");
- goto err_out0;
- }
-
- /* The reason we must manually set the PM status and usage counter is
- * we have just powered the device up but did not go through the PM
- * runtime resume callback, so we need to update these ourselves.
- */
- pm_runtime_get_noresume(pfdev->base.dev);
- 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;
-
- pm_runtime_put_autosuspend(pfdev->base.dev);
-
- return 0;
-
-err_out1:
- pm_runtime_dont_use_autosuspend(pfdev->base.dev);
- pm_runtime_disable(pfdev->base.dev);
- panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
- pm_runtime_put_noidle(pfdev->base.dev);
-err_out0:
- return err;
+ return panfrost_device_init(pfdev);
}
static void panfrost_remove(struct platform_device *pdev)
@@ -891,13 +847,7 @@ static void panfrost_remove(struct platform_device *pdev)
drm_dev_unregister(&pfdev->base);
- pm_runtime_get_sync(pfdev->base.dev);
- pm_runtime_dont_use_autosuspend(pfdev->base.dev);
- pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
- pm_runtime_put_noidle(pfdev->base.dev);
-
}
static ssize_t profiling_show(struct device *dev,
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v13 08/15] drm/panfrost: Move all DRM device initialisation into device_init()
2026-10-07 23:42 ` [PATCH v13 08/15] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
@ 2026-10-07 23:54 ` sashiko-bot
0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-10-07 23:54 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] drm/panfrost: UAF and MMIO faults on device unbind due to missing drm_dev_unplug
--
commit 6682d5cdca6ad86a6d0e64fd3ea14cdc2a997862
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Move all DRM device initialisation into device_init()
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.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 907d4a14a0b55..f77780c72a1ae 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
[ ... ]
> @@ -891,13 +847,7 @@ static void panfrost_remove(struct platform_device *pdev)
>
> drm_dev_unregister(&pfdev->base);
>
> - pm_runtime_get_sync(pfdev->base.dev);
> - pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> - pm_runtime_disable(pfdev->base.dev);
> panfrost_device_fini(pfdev);
[Severity: High]
Does this sequence cause a regression that leaves the driver vulnerable to
use-after-free and MMIO faults on device unbind?
When panfrost_remove() calls drm_dev_unregister(), it removes the device
node but does not revoke existing open file descriptors. The subsequent
call to panfrost_device_fini() shuts down the hardware, and after this
function returns, the driver core will free all devm allocations.
For example, panfrost_jm_init() allocates pfdev->js using devm_kzalloc:
drivers/gpu/drm/panfrost/panfrost_job.c:panfrost_jm_init() {
...
js = devm_kzalloc(pfdev->base.dev, sizeof(*js), GFP_KERNEL);
if (!js)
return -ENOMEM;
pfdev->js = js;
...
}
If a user unbinds the driver (e.g., via sysfs) while holding an open DRM
file descriptor, subsequent ioctls (like PANFROST_IOCTL_SUBMIT) could
access the freed pfdev->js pointer and unmapped or powered-off hardware
registers.
Should this teardown path use drm_dev_unplug() instead, and should the
driver add drm_dev_enter() and drm_dev_exit() guards to safely manage
concurrent ioctls during device removal?
> - pm_runtime_set_suspended(pfdev->base.dev);
> - pm_runtime_put_noidle(pfdev->base.dev);
> -
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-claude-fixes-v13-0-d6a493185860@collabora.com?part=8
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v13 09/15] drm/panfrost: Warn of RPM get failure at device remove time
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (7 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 08/15] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 10/15] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
` (5 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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 removal
not being able to take a PM reference, because it should be seen as fatal.
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 | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index a6deabb39fd2..687230225743 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"
@@ -385,7 +386,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_disable(pfdev->base.dev);
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v13 10/15] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (8 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 09/15] drm/panfrost: Warn of RPM get failure at device remove time Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-08 0:00 ` sashiko-bot
2026-10-07 23:42 ` [PATCH v13 11/15] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
` (4 subsequent siblings)
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
We must also disable the reset work item rather than simply cancelling
it, to prevent the knob from triggering another reset when the device
is being removed.
At the moment, the only legitimate input value for the knob is '1'. This is
to allow for future inputs that would encode alternative reset scenarios
we'd want to trigger manually.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 46 ++++++++++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_job.c | 2 +-
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 687230225743..b3dae6ce3eb0 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>
@@ -623,9 +624,54 @@ 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;
+
+ if (val != 1)
+ return -EINVAL;
+
+ ret = pm_runtime_get_if_active(pfdev->base.dev);
+ if (!ret)
+ return 0;
+
+ panfrost_device_schedule_reset(pfdev);
+ flush_work(&pfdev->reset.work);
+
+ /* ret < 0 means runtime PM for the device is disabled, so we
+ * only need to return the PM reference in the opposite case
+ */
+ if (ret > 0)
+ 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_gem_debugfs_init(minor);
panfrost_sched_debugfs_init(minor);
+ panfrost_reset_debugfs_init(minor);
}
#endif // CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 00fb36c577df..ab65786bf63b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -932,7 +932,7 @@ void panfrost_jm_fini(struct panfrost_device *pfdev)
drm_sched_fini(&js->queue[j].sched);
}
- cancel_work_sync(&pfdev->reset.work);
+ disable_work_sync(&pfdev->reset.work);
destroy_workqueue(pfdev->reset.wq);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v13 10/15] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-10-07 23:42 ` [PATCH v13 10/15] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-10-08 0:00 ` sashiko-bot
0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-10-08 0:00 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] drm/panfrost: UAF in reset work due to incorrect teardown order
--
commit cfa26c5946307af48b39c322e0d8f2fd03c46e05
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Add debugfs knob for manually triggering a GPU reset
This patch adds a debugfs knob to manually trigger a GPU reset for testing
purposes. It also updates the teardown path to disable rather than cancel
the reset work to prevent the knob from triggering resets during device removal.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index 00fb36c577df7..ab65786bf63bc 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
> @@ -932,7 +932,7 @@ void panfrost_jm_fini(struct panfrost_device *pfdev)
> drm_sched_fini(&js->queue[j].sched);
> }
>
> - cancel_work_sync(&pfdev->reset.work);
> + disable_work_sync(&pfdev->reset.work);
[Severity: High]
Does this teardown order leave a window for a use-after-free if a reset is
triggered during unbinding?
In panfrost_jm_fini(), the DRM schedulers are destroyed via drm_sched_fini()
before the asynchronous reset work is disabled. The drm_sched_fini() function
tears down the scheduler, destroying sched->submit_wq and freeing
sched->sched_rq.
If a GPU reset is scheduled right before or during driver unbinding (for
example, by writing to the newly introduced debugfs reset knob), the reset
work can run asynchronously while panfrost_jm_fini() executes. The reset
work calls drm_sched_stop() and drm_sched_start(), which ultimately queues
work onto the scheduler's now-destroyed submit_wq, leading to a crash.
Should disable_work_sync() be called before the loop that destroys the DRM
schedulers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-claude-fixes-v13-0-d6a493185860@collabora.com?part=10
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v13 11/15] drm/panfrost: Move perfcnt GPU disable sequence into a helper
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (9 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 10/15] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 12/15] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
` (3 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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] 22+ messages in thread* [PATCH v13 12/15] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (10 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 11/15] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 13/15] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
` (2 subsequent siblings)
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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. In the usual case,
before a perfcnt dump is requested, there will be no stale lines in GPU
caches for the sample BO's physmem pages, since their memory had not
recently been addressed by the GPU.
However, in the off-chance that the sample BO reuses memory that the GPU
had recently addressed and for which it contains stale lines in its caches,
there already was a FLUSH_PT command being issued as part of the buffer GPU
mapping procedure, so by the time panfrost_gem_open() comes back at perfcnt
session enable, stale GPU cache lines should've already been evicted.
In either case, forcing a GPU cache clean when enabling a new session makes
no difference, and removing it simplifies handling of the error path.
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 | 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] 22+ messages in thread* [PATCH v13 13/15] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (11 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 12/15] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:55 ` sashiko-bot
2026-10-07 23:42 ` [PATCH v13 14/15] drm/panfrost: Introduce a reset lock Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 15/15] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
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] 22+ messages in thread* Re: [PATCH v13 13/15] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-10-07 23:42 ` [PATCH v13 13/15] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-10-07 23:55 ` sashiko-bot
0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-10-07 23:55 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] drm: race condition in panfrost_gpu_irq_handler causes lost perfcnt interrupts
--
commit 54ca68cf4bae3920aff016a6d2b492d879450341
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
This commit skips the GPU cache flush after a performance counter sample
becomes available when the GPU and CPU are fully coherent. It does this by
completing the dump immediately rather than issuing a cache clean command.
> 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);
[Severity: Medium]
Since complete() is now called before the IRQ handler clears the interrupt,
could this cause a race condition where a subsequent perfcnt dump request
times out?
If the waiting process wakes up and immediately issues another perfcnt dump
request on another CPU, it might write GPU_CMD_PERFCNT_SAMPLE and trigger a
new GPU_IRQ_PERFCNT_SAMPLE_COMPLETED interrupt before the original IRQ
handler finishes.
The original IRQ handler in panfrost_gpu_irq_handler() finishes by writing
the previously read state back:
drivers/gpu/drm/panfrost/panfrost_gpu.c:panfrost_gpu_irq_handler() {
...
state = gpu_read(pfdev, GPU_INT_STAT);
...
if (state & GPU_IRQ_PERFCNT_SAMPLE_COMPLETED)
panfrost_perfcnt_sample_done(pfdev);
...
gpu_write(pfdev, GPU_INT_CLEAR, state);
return IRQ_HANDLED;
}
Could this gpu_write(pfdev, GPU_INT_CLEAR, state) clear the newly asserted
interrupt, causing the second dump request to hang and return -ETIMEDOUT to
userspace?
> }
>
> static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-claude-fixes-v13-0-d6a493185860@collabora.com?part=13
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v13 14/15] drm/panfrost: Introduce a reset lock
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (12 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 13/15] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:42 ` [PATCH v13 15/15] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
14 siblings, 0 replies; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
In order 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 timestamp.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.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 b3dae6ce3eb0..5718e8185427 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -509,6 +509,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
{
+ guard(rwsem_write)(&pfdev->reset.lock);
+
panfrost_gpu_soft_reset(pfdev);
panfrost_gpu_power_on(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index a0b9a2145fc9..63452febb1cf 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 ab65786bf63b..4ddf4b722ddf 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -877,6 +877,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] 22+ messages in thread* [PATCH v13 15/15] drm/panfrost: Fix races between perfcnt and reset sequence
2026-10-07 23:42 [PATCH v13 00/15] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (13 preceding siblings ...)
2026-10-07 23:42 ` [PATCH v13 14/15] drm/panfrost: Introduce a reset lock Adrián Larumbe
@ 2026-10-07 23:42 ` Adrián Larumbe
2026-10-07 23:58 ` sashiko-bot
14 siblings, 1 reply; 22+ messages in thread
From: Adrián Larumbe @ 2026-10-07 23:42 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,
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 when disabling and re-enabling perfcnt
configuration, and also in the sections inside the 'enable'and 'dump'
ioctls where device registers are being accessed.
On top of that, expand the DRM uAPI for the perfcnt DUMP operation
so that user space can be made aware of a reset having happened,
whether it succeeded or failed to restore the original configuration.
UM needs to know about this condition because counter data is inaccurate
after a reset, so the best approach might be simply to try again.
Finally, update driver uAPI documentation to explain the meaning of the
new perfcnt dump ioctl's state flags, and bump DRM driver minor number
to reflect the new DUMP IOCTL req field.
Fixes: 73e467f60acd ("drm/panfrost: Consolidate reset handling")
Fixes: 7786fd108777 ("drm/panfrost: Expose performance counters through unstable ioctls")
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 2 +
drivers/gpu/drm/panfrost/panfrost_drv.c | 3 +-
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 269 +++++++++++++++++++---------
drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
include/uapi/drm/panfrost_drm.h | 29 ++-
5 files changed, 218 insertions(+), 86 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 5718e8185427..fb0a1b45007b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -519,6 +519,8 @@ void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
panfrost_jm_reset_interrupts(pfdev);
if (enable_job_int)
panfrost_jm_enable_interrupts(pfdev);
+
+ panfrost_perfcnt_reset(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 571a26b84126..de9b1c115181 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 state 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,
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index b3f71d7fd82a..b5d9e5862e37 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Collabora Ltd */
+#include "asm-generic/errno-base.h"
#include <linux/completion.h>
#include <linux/iopoll.h>
#include <linux/iosys-map.h>
@@ -11,6 +12,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"
@@ -28,21 +30,31 @@
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;
+ u32 state;
+ bool owns_as_ref;
};
static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+
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);
+
+ if (perfcnt->owns_as_ref) {
+ panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
+ perfcnt->owns_as_ref = false;
+ }
}
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
@@ -58,27 +70,130 @@ 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;
- 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);
+ lockdep_assert_held(&pfdev->reset.lock);
+ drm_WARN_ON(&pfdev->base, perfcnt->owns_as_ref);
+
+ ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ if (ret < 0)
+ return ret;
+
+ perfcnt->owns_as_ref = true;
+
+ 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, u32 *state)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
+ int ret;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *state = perfcnt->state;
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD)
+ return -EIO;
+
+ perfcnt->state = 0;
+
+ 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);
+ }
+
+ /*
+ * Here we release the reset semaphore because perfcnt should not get in the way
+ * of a HW reset. Besides, a legitimate reset might be issued during the wait.
+ */
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
+
+ /* A reset might come through in the gap between the completion returning and the following
+ * check, but because no sample was produced, we don't care to relay the state back to UM.
+ */
if (!ret)
ret = -ETIMEDOUT;
- else if (ret > 0)
- ret = 0;
- return ret;
+ /* We should not touch perfcnt's state when the completion wait was interrupted by UM */
+ if (ret < 0)
+ return ret;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *state |= perfcnt->state;
+
+ /* UM must re-enable their session before requesting new dumps. */
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD)
+ return -EIO;
+
+ /* If we faced a reset during our SAMPLE, the user needs to try again. */
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET)
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
+ struct drm_file *file_priv)
+{
+ struct panfrost_file_priv *user = file_priv->driver_priv;
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf);
+
+ if (user != perfcnt->user)
+ return -EINVAL;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_perfcnt_hw_disable(pfdev);
+ 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_gem_mapping_put(perfcnt->mapping);
+ perfcnt->mapping = NULL;
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+
+ return 0;
}
static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
@@ -87,14 +202,24 @@ 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)
+ if (perfcnt->user == user) {
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) {
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (!ret)
+ perfcnt->state = 0;
+ return ret;
+ }
+ }
+
return 0;
- else if (perfcnt->user)
+ }
+
+ if (perfcnt->user)
return -EBUSY;
ret = pm_runtime_get_sync(pfdev->base.dev);
@@ -122,54 +247,30 @@ 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");
- /*
- * Clear the counters to start from a fresh state.
- */
- 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);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ /*
+ * Clear the counters to start from a fresh state.
+ */
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ goto err_vunmap;
- if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+ perfcnt->user = user;
+ perfcnt->state = 0;
+ }
/* The BO ref is retained by the mapping. */
drm_gem_object_put(&bo->base);
- perfcnt->user = user;
-
return 0;
err_vunmap:
@@ -185,30 +286,6 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
return ret;
}
-static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
- struct drm_file *file_priv)
-{
- struct panfrost_file_priv *user = file_priv->driver_priv;
- struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf);
-
- if (user != perfcnt->user)
- return -EINVAL;
-
- panfrost_perfcnt_hw_disable(pfdev);
-
- 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);
-
- return 0;
-}
-
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -249,13 +326,16 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
if (ret)
return ret;
+ if (req->pad)
+ return -EINVAL;
+
mutex_lock(&perfcnt->lock);
if (perfcnt->user != file_priv->driver_priv) {
ret = -EINVAL;
goto out;
}
- ret = panfrost_perfcnt_dump_locked(pfdev);
+ ret = panfrost_perfcnt_dump_locked(pfdev, &req->state);
if (ret)
goto out;
@@ -322,14 +402,13 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
return -ENOMEM;
perfcnt->bosize = size;
+ pfdev->perfcnt = perfcnt;
+ mutex_init(&perfcnt->lock);
+ init_completion(&perfcnt->dump_comp);
/* Start with everything disabled. */
panfrost_perfcnt_hw_disable(pfdev);
- init_completion(&perfcnt->dump_comp);
- mutex_init(&perfcnt->lock);
- pfdev->perfcnt = perfcnt;
-
return 0;
}
@@ -338,3 +417,25 @@ 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;
+
+ /* All active AS are released during the MMU post_reset. */
+ perfcnt->owns_as_ref = false;
+ perfcnt->state |= PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET;
+ if (panfrost_perfcnt_hw_enable(pfdev))
+ perfcnt->state |= PANFROST_PERFCNT_SESSION_DEAD;
+
+ /* Unblock pending sample requests. */
+ 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..7831c48c59c0 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)
@@ -270,8 +270,35 @@ struct drm_panfrost_perfcnt_enable {
__u32 counterset;
};
+/*
+ * The next two flags describe the state of a perfcnt dump request
+ * as influenced by a device reset. They are the only values the
+ * perfcnt_dump ioctl state field can take on.
+ * Only certain state and ioctl retval combinations are legitimate.
+ */
+
+/* A new perfcnt_enable ioctl should be issued before requesting
+ * more dumps, because a HW reset failed to recreate perfcnt's
+ * original state. Otherwise further perfcnt_dump's will fail.
+ * This flag being set means ioctl's retval is always -EIO.
+ */
+#define PANFROST_PERFCNT_SESSION_DEAD (1 << 0)
+
+/* A HW reset happened before or during a sample request, and
+ * perfcnt's internal state was successfully restored. There
+ * are two possible outcomes depending on the ioctl's retval:
+ * 0: A reset happened before a dump was requested, but did
+ * nonetheless succeed. Counter values are relative to last reset.
+ * -EAGAIN: A reset happened when a counter values sampling
+ * request was ongoing. Values are undefined so a new dump ioctl
+ * should be issued.
+ */
+#define PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET (1 << 1)
+
struct drm_panfrost_perfcnt_dump {
__u64 buf_ptr;
+ __u32 state;
+ __u32 pad; /* MBZ */
};
/* madvise provides a way to tell the kernel in case a buffers contents
--
2.55.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v13 15/15] drm/panfrost: Fix races between perfcnt and reset sequence
2026-10-07 23:42 ` [PATCH v13 15/15] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
@ 2026-10-07 23:58 ` sashiko-bot
0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-10-07 23:58 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] drm/panfrost: sticky DEAD flag causes AS reference leak on multiple GPU resets
- [High] drm/panfrost: UAF in perfcnt context due to devres lifecycle mismatch
- [Medium] drm/panfrost: perfcnt->state is destroyed on ERESTARTSYS signal interrupt in panfrost_perfcnt_dump_locked()
- [Low] drm/panfrost: cleanup.md violation by mixing goto and scoped_guard in panfrost_perfcnt_enable_locked
--
commit be4c8f35c54f411638ee240c72cfc88e3f4b3c5d
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix races between perfcnt and reset sequence
This commit updates the Panfrost driver to use the reset rw_semaphore
when configuring performance counters and during dump ioctls to avoid
races with the reset sequence. It also expands the DUMP uAPI to report
reset states back to userspace and bumps the DRM minor version.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> @@ -118,12 +118,22 @@ static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev, u32 *stat
> {
[ ... ]
> scoped_guard(rwsem_read, &pfdev->reset.lock) {
> *state = perfcnt->state;
> if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD)
> return -EIO;
>
> perfcnt->state = 0;
>
> reinit_completion(&pfdev->perfcnt->dump_comp);
[ ... ]
> ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
> msecs_to_jiffies(1000));
>
> /* A reset might come through in the gap between the completion returning and the following
> * check, but because no sample was produced, we don't care to relay the state back to UM.
> */
> if (!ret)
> ret = -ETIMEDOUT;
>
> /* We should not touch perfcnt's state when the completion wait was interrupted by UM */
> if (ret < 0)
> return ret;
[Severity: Medium]
Could the perfcnt state be permanently lost if the completion wait is
interrupted by a signal like -ERESTARTSYS?
The state is cleared before the wait, and if ret is less than zero, the
function returns early without restoring it. When the kernel restarts the
ioctl, userspace might miss the reset flag because the state has already
been zeroed out.
[ ... ]
> @@ -182,10 +182,19 @@
> static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> struct drm_file *file_priv,
> unsigned int counterset)
> {
[ ... ]
> if (perfcnt->user == user) {
> scoped_guard(rwsem_read, &pfdev->reset.lock) {
> if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) {
> ret = panfrost_perfcnt_hw_enable(pfdev);
> if (!ret)
> perfcnt->state = 0;
> return ret;
> }
> }
>
> return 0;
> }
[Severity: High]
Does this cause an Address Space reference leak after multiple resets?
If a prior hardware reset fails, the PANFROST_PERFCNT_SESSION_DEAD flag
is set in panfrost_perfcnt_reset(). A subsequent successful reset drops
owns_as_ref but does not clear the dead flag.
If userspace then issues an enable ioctl, it takes this path and calls
panfrost_perfcnt_hw_enable() which observes owns_as_ref is already true,
and takes a second address space reference without releasing the first.
[ ... ]
> @@ -247,16 +247,15 @@
> panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
>
> scoped_guard(rwsem_read, &pfdev->reset.lock) {
> /*
> * Clear the counters to start from a fresh state.
> */
> gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
>
> ret = panfrost_perfcnt_hw_enable(pfdev);
> if (ret)
> goto err_vunmap;
[Severity: Low]
Is it safe to jump out of a scoped guard using a goto label?
If panfrost_perfcnt_hw_enable() fails, it jumps out of the scoped guard to
the err_vunmap label. The cleanup subsystem guide recommends not mixing
goto-based error handling and scope-based cleanups in the same function
due to the potential for confusing ownership semantics.
[ ... ]
> @@ -326,9 +326,14 @@
> int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
> struct drm_file *file_priv)
> {
> struct panfrost_device *pfdev = to_panfrost_device(dev);
> struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> struct drm_panfrost_perfcnt_dump *req = data;
> void __user *user_ptr = (void __user *)(uintptr_t)req->buf_ptr;
> int ret;
>
> ret = panfrost_unstable_ioctl_check();
> if (ret)
> return ret;
>
> + if (req->pad)
> + return -EINVAL;
> +
> mutex_lock(&perfcnt->lock);
[Severity: High]
Might this trigger a memory corruption if the device is unbound while the
DRM file descriptor remains open?
The perfcnt structure is allocated with devm_kzalloc() tied to the platform
device pfdev->base.dev, so it is freed when the device unbinds. However,
the DRM file operations can outlive the platform device.
When userspace calls this ioctl later, it unconditionally dereferences
pfdev->perfcnt and attempts to acquire perfcnt->lock. Could this be
protected with a drm_dev_enter() unplug check?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-claude-fixes-v13-0-d6a493185860@collabora.com?part=15
^ permalink raw reply [flat|nested] 22+ messages in thread