* [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 11:28 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
` (15 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Since the moment we call drm_dev_register() the device should be in a
position to accept jobs, so it's best if the shrinker is already
initialized by then.
On top of that, make shrinker functions take an panfrost_device pointer
like other functions in the same sequence and rename them accordingly.
Essentially mimic the init/fini behaviour in Panthor.
On top of that, remove the config feature check, because it's unnecessary.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 8 +++++++-
drivers/gpu/drm/panfrost/panfrost_drv.c | 6 ------
drivers/gpu/drm/panfrost/panfrost_drv.h | 4 ++++
drivers/gpu/drm/panfrost/panfrost_gem.c | 25 ++++++++++++++----------
drivers/gpu/drm/panfrost/panfrost_gem.h | 7 ++++---
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 8 ++------
6 files changed, 32 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 485349faf251..05c40d5a20b5 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -280,9 +280,14 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_job;
- panfrost_gem_init(pfdev);
+ err = panfrost_gem_init(pfdev);
+ if (err)
+ goto out_perfcnt;
return 0;
+
+out_perfcnt:
+ panfrost_perfcnt_fini(pfdev);
out_job:
panfrost_jm_fini(pfdev);
out_mmu:
@@ -304,6 +309,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
+ panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
panfrost_mmu_fini(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 96a66d673ba6..9882a3ede75f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -1003,14 +1003,9 @@ static int panfrost_probe(struct platform_device *pdev)
if (err < 0)
goto err_out1;
- err = panfrost_gem_shrinker_init(&pfdev->base);
- if (err)
- goto err_out2;
return 0;
-err_out2:
- drm_dev_unregister(&pfdev->base);
err_out1:
pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
@@ -1024,7 +1019,6 @@ static void panfrost_remove(struct platform_device *pdev)
struct panfrost_device *pfdev = platform_get_drvdata(pdev);
drm_dev_unregister(&pfdev->base);
- panfrost_gem_shrinker_cleanup(&pfdev->base);
pm_runtime_get_sync(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
index a2277ec61aab..bac0c415d4da 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.h
@@ -4,6 +4,10 @@
#ifndef __PANFROST_DRV_H__
#define __PANFROST_DRV_H__
+#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
extern bool panfrost_transparent_hugepage;
+#else
+#define panfrost_transparent_hugepage false
+#endif
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3a7fce428898..7b993a089af2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -15,20 +15,25 @@
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
-void panfrost_gem_init(struct panfrost_device *pfdev)
+int panfrost_gem_init(struct panfrost_device *pfdev)
{
int err;
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
- !panfrost_transparent_hugepage)
- return;
+ if (panfrost_transparent_hugepage) {
+ err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
+ if (drm_gem_get_huge_mnt(&pfdev->base))
+ drm_info(&pfdev->base, "Using Transparent Hugepage\n");
+ else if (err)
+ drm_warn(&pfdev->base,
+ "Can't use Transparent Hugepage (%d)\n", err);
+ }
- err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
- if (drm_gem_get_huge_mnt(&pfdev->base))
- drm_info(&pfdev->base, "Using Transparent Hugepage\n");
- else if (err)
- drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n",
- err);
+ return panfrost_gem_shrinker_init(pfdev);
+}
+
+void panfrost_gem_fini(struct panfrost_device *pfdev)
+{
+ panfrost_gem_shrinker_fini(pfdev);
}
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 79d4377019e9..5c823cdbd980 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
return container_of(node, struct panfrost_gem_mapping, mmnode);
}
-void panfrost_gem_init(struct panfrost_device *pfdev);
+int panfrost_gem_init(struct panfrost_device *pfdev);
+void panfrost_gem_fini(struct panfrost_device *pfdev);
struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
@@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
-int panfrost_gem_shrinker_init(struct drm_device *dev);
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev);
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev);
void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label);
int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 2fe967a90bcb..fefae87535d6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -95,10 +95,8 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*
* This function registers and sets up the panfrost shrinker.
*/
-int panfrost_gem_shrinker_init(struct drm_device *dev)
+int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
-
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
return -ENOMEM;
@@ -118,10 +116,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
*
* This function unregisters the panfrost shrinker.
*/
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
+void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = to_panfrost_device(dev);
-
if (pfdev->shrinker)
shrinker_free(pfdev->shrinker);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down
2026-08-28 20:56 ` [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-09-01 11:28 ` Boris Brezillon
2026-09-02 15:36 ` Adrián Larumbe
0 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 11:28 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:41 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Since the moment we call drm_dev_register() the device should be in a
> position to accept jobs, so it's best if the shrinker is already
> initialized by then.
>
> On top of that, make shrinker functions take an panfrost_device pointer
> like other functions in the same sequence and rename them accordingly.
>
> Essentially mimic the init/fini behaviour in Panthor.
>
> On top of that, remove the config feature check, because it's unnecessary.
I don't think that's accurate anymore (we still have a config check,
except it's encoded directly in the panfrost_transparent_hugepage definition),
and I think I'd prefer if this change was done in a separate commit.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 8 +++++++-
> drivers/gpu/drm/panfrost/panfrost_drv.c | 6 ------
> drivers/gpu/drm/panfrost/panfrost_drv.h | 4 ++++
> drivers/gpu/drm/panfrost/panfrost_gem.c | 25 ++++++++++++++----------
> drivers/gpu/drm/panfrost/panfrost_gem.h | 7 ++++---
> drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 8 ++------
> 6 files changed, 32 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 485349faf251..05c40d5a20b5 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -280,9 +280,14 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> if (err)
> goto out_job;
>
> - panfrost_gem_init(pfdev);
> + err = panfrost_gem_init(pfdev);
> + if (err)
> + goto out_perfcnt;
>
> return 0;
> +
> +out_perfcnt:
> + panfrost_perfcnt_fini(pfdev);
> out_job:
> panfrost_jm_fini(pfdev);
> out_mmu:
> @@ -304,6 +309,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> + panfrost_gem_fini(pfdev);
> panfrost_perfcnt_fini(pfdev);
> panfrost_jm_fini(pfdev);
> panfrost_mmu_fini(pfdev);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 96a66d673ba6..9882a3ede75f 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> @@ -1003,14 +1003,9 @@ static int panfrost_probe(struct platform_device *pdev)
> if (err < 0)
> goto err_out1;
>
> - err = panfrost_gem_shrinker_init(&pfdev->base);
> - if (err)
> - goto err_out2;
>
> return 0;
>
> -err_out2:
> - drm_dev_unregister(&pfdev->base);
> err_out1:
> pm_runtime_disable(pfdev->base.dev);
> panfrost_device_fini(pfdev);
> @@ -1024,7 +1019,6 @@ static void panfrost_remove(struct platform_device *pdev)
> struct panfrost_device *pfdev = platform_get_drvdata(pdev);
>
> drm_dev_unregister(&pfdev->base);
> - panfrost_gem_shrinker_cleanup(&pfdev->base);
>
> pm_runtime_get_sync(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
> index a2277ec61aab..bac0c415d4da 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.h
> @@ -4,6 +4,10 @@
> #ifndef __PANFROST_DRV_H__
> #define __PANFROST_DRV_H__
>
> +#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
> extern bool panfrost_transparent_hugepage;
> +#else
> +#define panfrost_transparent_hugepage false
> +#endif
>
> #endif
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> index 3a7fce428898..7b993a089af2 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> @@ -15,20 +15,25 @@
> #include "panfrost_gem.h"
> #include "panfrost_mmu.h"
>
> -void panfrost_gem_init(struct panfrost_device *pfdev)
> +int panfrost_gem_init(struct panfrost_device *pfdev)
> {
> int err;
>
> - if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> - !panfrost_transparent_hugepage)
> - return;
> + if (panfrost_transparent_hugepage) {
> + err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
> + if (drm_gem_get_huge_mnt(&pfdev->base))
> + drm_info(&pfdev->base, "Using Transparent Hugepage\n");
> + else if (err)
> + drm_warn(&pfdev->base,
> + "Can't use Transparent Hugepage (%d)\n", err);
This can probably be turned into:
if (err)
drm_warn(&pfdev->base,
"Can't use Transparent Hugepage (%d)\n", err);
else
drm_info(&pfdev->base, "Using Transparent Hugepage\n");
since there's no world where drm_gem_get_huge_mnt() would return NULL if
drm_gem_huge_mnt_create() returns 0 when CONFIG_TRANSPARENT_HUGEPAGE=y.
The pre-existing checks where there to cover CONFIG_TRANSPARENT_HUGEPAGE=n
which you already cover by not entering this branch.
> + }
>
> - err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
> - if (drm_gem_get_huge_mnt(&pfdev->base))
> - drm_info(&pfdev->base, "Using Transparent Hugepage\n");
> - else if (err)
> - drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n",
> - err);
> + return panfrost_gem_shrinker_init(pfdev);
> +}
> +
> +void panfrost_gem_fini(struct panfrost_device *pfdev)
> +{
> + panfrost_gem_shrinker_fini(pfdev);
> }
>
> #ifdef CONFIG_DEBUG_FS
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
> index 79d4377019e9..5c823cdbd980 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
> @@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
> return container_of(node, struct panfrost_gem_mapping, mmnode);
> }
>
> -void panfrost_gem_init(struct panfrost_device *pfdev);
> +int panfrost_gem_init(struct panfrost_device *pfdev);
> +void panfrost_gem_fini(struct panfrost_device *pfdev);
>
> struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
>
> @@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
> void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
> void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
>
> -int panfrost_gem_shrinker_init(struct drm_device *dev);
> -void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
> +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev);
> +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev);
>
> void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label);
> int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> index 2fe967a90bcb..fefae87535d6 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> @@ -95,10 +95,8 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
> *
> * This function registers and sets up the panfrost shrinker.
> */
> -int panfrost_gem_shrinker_init(struct drm_device *dev)
> +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
> {
> - struct panfrost_device *pfdev = to_panfrost_device(dev);
> -
> pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
> if (!pfdev->shrinker)
> return -ENOMEM;
> @@ -118,10 +116,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
> *
> * This function unregisters the panfrost shrinker.
> */
> -void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
> +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev)
> {
> - struct panfrost_device *pfdev = to_panfrost_device(dev);
> -
> if (pfdev->shrinker)
> shrinker_free(pfdev->shrinker);
> }
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down
2026-09-01 11:28 ` Boris Brezillon
@ 2026-09-02 15:36 ` Adrián Larumbe
0 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:36 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 13:28, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:41 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > Since the moment we call drm_dev_register() the device should be in a
> > position to accept jobs, so it's best if the shrinker is already
> > initialized by then.
> >
> > On top of that, make shrinker functions take an panfrost_device pointer
> > like other functions in the same sequence and rename them accordingly.
> >
> > Essentially mimic the init/fini behaviour in Panthor.
> >
> > On top of that, remove the config feature check, because it's unnecessary.
>
> I don't think that's accurate anymore (we still have a config check,
> except it's encoded directly in the panfrost_transparent_hugepage definition),
> and I think I'd prefer if this change was done in a separate commit.
Maybe I could do it in the commit where I move that module param's handling into panfrost_gem?
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 8 +++++++-
> > drivers/gpu/drm/panfrost/panfrost_drv.c | 6 ------
> > drivers/gpu/drm/panfrost/panfrost_drv.h | 4 ++++
> > drivers/gpu/drm/panfrost/panfrost_gem.c | 25 ++++++++++++++----------
> > drivers/gpu/drm/panfrost/panfrost_gem.h | 7 ++++---
> > drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 8 ++------
> > 6 files changed, 32 insertions(+), 26 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index 485349faf251..05c40d5a20b5 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > @@ -280,9 +280,14 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > if (err)
> > goto out_job;
> >
> > - panfrost_gem_init(pfdev);
> > + err = panfrost_gem_init(pfdev);
> > + if (err)
> > + goto out_perfcnt;
> >
> > return 0;
> > +
> > +out_perfcnt:
> > + panfrost_perfcnt_fini(pfdev);
> > out_job:
> > panfrost_jm_fini(pfdev);
> > out_mmu:
> > @@ -304,6 +309,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> >
> > void panfrost_device_fini(struct panfrost_device *pfdev)
> > {
> > + panfrost_gem_fini(pfdev);
> > panfrost_perfcnt_fini(pfdev);
> > panfrost_jm_fini(pfdev);
> > panfrost_mmu_fini(pfdev);
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > index 96a66d673ba6..9882a3ede75f 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > @@ -1003,14 +1003,9 @@ static int panfrost_probe(struct platform_device *pdev)
> > if (err < 0)
> > goto err_out1;
> >
> > - err = panfrost_gem_shrinker_init(&pfdev->base);
> > - if (err)
> > - goto err_out2;
> >
> > return 0;
> >
> > -err_out2:
> > - drm_dev_unregister(&pfdev->base);
> > err_out1:
> > pm_runtime_disable(pfdev->base.dev);
> > panfrost_device_fini(pfdev);
> > @@ -1024,7 +1019,6 @@ static void panfrost_remove(struct platform_device *pdev)
> > struct panfrost_device *pfdev = platform_get_drvdata(pdev);
> >
> > drm_dev_unregister(&pfdev->base);
> > - panfrost_gem_shrinker_cleanup(&pfdev->base);
> >
> > pm_runtime_get_sync(pfdev->base.dev);
> > pm_runtime_disable(pfdev->base.dev);
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
> > index a2277ec61aab..bac0c415d4da 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_drv.h
> > +++ b/drivers/gpu/drm/panfrost/panfrost_drv.h
> > @@ -4,6 +4,10 @@
> > #ifndef __PANFROST_DRV_H__
> > #define __PANFROST_DRV_H__
> >
> > +#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
> > extern bool panfrost_transparent_hugepage;
> > +#else
> > +#define panfrost_transparent_hugepage false
> > +#endif
> >
> > #endif
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> > index 3a7fce428898..7b993a089af2 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> > @@ -15,20 +15,25 @@
> > #include "panfrost_gem.h"
> > #include "panfrost_mmu.h"
> >
> > -void panfrost_gem_init(struct panfrost_device *pfdev)
> > +int panfrost_gem_init(struct panfrost_device *pfdev)
> > {
> > int err;
> >
> > - if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
> > - !panfrost_transparent_hugepage)
> > - return;
> > + if (panfrost_transparent_hugepage) {
> > + err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
> > + if (drm_gem_get_huge_mnt(&pfdev->base))
> > + drm_info(&pfdev->base, "Using Transparent Hugepage\n");
> > + else if (err)
> > + drm_warn(&pfdev->base,
> > + "Can't use Transparent Hugepage (%d)\n", err);
>
> This can probably be turned into:
>
> if (err)
> drm_warn(&pfdev->base,
> "Can't use Transparent Hugepage (%d)\n", err);
> else
> drm_info(&pfdev->base, "Using Transparent Hugepage\n");
>
> since there's no world where drm_gem_get_huge_mnt() would return NULL if
> drm_gem_huge_mnt_create() returns 0 when CONFIG_TRANSPARENT_HUGEPAGE=y.
> The pre-existing checks where there to cover CONFIG_TRANSPARENT_HUGEPAGE=n
> which you already cover by not entering this branch.
Will do, but also write this into the patch that moves the module param into panfrost_gem.c instead.
> > + }
> >
> > - err = drm_gem_huge_mnt_create(&pfdev->base, "within_size");
> > - if (drm_gem_get_huge_mnt(&pfdev->base))
> > - drm_info(&pfdev->base, "Using Transparent Hugepage\n");
> > - else if (err)
> > - drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n",
> > - err);
> > + return panfrost_gem_shrinker_init(pfdev);
> > +}
> > +
> > +void panfrost_gem_fini(struct panfrost_device *pfdev)
> > +{
> > + panfrost_gem_shrinker_fini(pfdev);
> > }
> >
> > #ifdef CONFIG_DEBUG_FS
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
> > index 79d4377019e9..5c823cdbd980 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_gem.h
> > +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
> > @@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
> > return container_of(node, struct panfrost_gem_mapping, mmnode);
> > }
> >
> > -void panfrost_gem_init(struct panfrost_device *pfdev);
> > +int panfrost_gem_init(struct panfrost_device *pfdev);
> > +void panfrost_gem_fini(struct panfrost_device *pfdev);
> >
> > struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
> >
> > @@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
> > void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
> > void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
> >
> > -int panfrost_gem_shrinker_init(struct drm_device *dev);
> > -void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
> > +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev);
> > +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev);
> >
> > void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label);
> > int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> > index 2fe967a90bcb..fefae87535d6 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> > @@ -95,10 +95,8 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
> > *
> > * This function registers and sets up the panfrost shrinker.
> > */
> > -int panfrost_gem_shrinker_init(struct drm_device *dev)
> > +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
> > {
> > - struct panfrost_device *pfdev = to_panfrost_device(dev);
> > -
> > pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
> > if (!pfdev->shrinker)
> > return -ENOMEM;
> > @@ -118,10 +116,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
> > *
> > * This function unregisters the panfrost shrinker.
> > */
> > -void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
> > +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev)
> > {
> > - struct panfrost_device *pfdev = to_panfrost_device(dev);
> > -
> > if (pfdev->shrinker)
> > shrinker_free(pfdev->shrinker);
> > }
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init()
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 11:49 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
` (14 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Ideally the probe() function will do as little as possible, and all device
initialisation and registration should happen inside the panfrost device
subsystem, just like it's done in Panthor. This also simplifies resource
unwinding in the error path.
Do the same thing for DRM driver remove, as in, sweep most of the action
into panfrost_device_fini(), just like we did for device probe.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 33 ++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_drv.c | 44 +-----------------------------
2 files changed, 34 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 05c40d5a20b5..d2d2830f11a7 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -8,6 +8,7 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <drm/drm_drv.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -216,6 +217,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
{
int err;
+ pfdev->comp = of_device_get_match_data(pfdev->base.dev);
+ if (!pfdev->comp)
+ return -ENODEV;
+
+ pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
+
+ mutex_init(&pfdev->shrinker_lock);
+ INIT_LIST_HEAD(&pfdev->shrinker_list);
+
mutex_init(&pfdev->sched_lock);
INIT_LIST_HEAD(&pfdev->as_lru_list);
@@ -284,8 +294,25 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_perfcnt;
+ pm_runtime_set_active(pfdev->base.dev);
+ pm_runtime_mark_last_busy(pfdev->base.dev);
+ pm_runtime_enable(pfdev->base.dev);
+ pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
+ pm_runtime_use_autosuspend(pfdev->base.dev);
+
+ /*
+ * Register the DRM device with the core and the connectors with
+ * sysfs
+ */
+ err = drm_dev_register(&pfdev->base, 0);
+ if (err < 0)
+ goto out_devreg;
+
return 0;
+out_devreg:
+ pm_runtime_disable(pfdev->base.dev);
+ panfrost_gem_fini(pfdev);
out_perfcnt:
panfrost_perfcnt_fini(pfdev);
out_job:
@@ -304,11 +331,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
+ pm_runtime_set_suspended(pfdev->base.dev);
return err;
}
void panfrost_device_fini(struct panfrost_device *pfdev)
{
+ pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
+
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
@@ -319,6 +350,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_clk_fini(pfdev);
panfrost_reset_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
+
+ pm_runtime_set_suspended(pfdev->base.dev);
}
#define PANFROST_EXCEPTION(id) \
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 9882a3ede75f..80996e311a9d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -964,7 +964,6 @@ MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with T
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
- int err;
pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
struct panfrost_device, base);
@@ -973,45 +972,7 @@ static int panfrost_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pfdev);
- pfdev->comp = of_device_get_match_data(&pdev->dev);
- if (!pfdev->comp)
- return -ENODEV;
-
- pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
-
- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
- err = panfrost_device_init(pfdev);
- if (err) {
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Fatal error during GPU init\n");
- goto err_out0;
- }
-
- pm_runtime_set_active(pfdev->base.dev);
- pm_runtime_mark_last_busy(pfdev->base.dev);
- pm_runtime_enable(pfdev->base.dev);
- pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
- pm_runtime_use_autosuspend(pfdev->base.dev);
-
- /*
- * Register the DRM device with the core and the connectors with
- * sysfs
- */
- err = drm_dev_register(&pfdev->base, 0);
- if (err < 0)
- goto err_out1;
-
-
- return 0;
-
-err_out1:
- pm_runtime_disable(pfdev->base.dev);
- panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
-err_out0:
- return err;
+ return panfrost_device_init(pfdev);
}
static void panfrost_remove(struct platform_device *pdev)
@@ -1020,10 +981,7 @@ static void panfrost_remove(struct platform_device *pdev)
drm_dev_unregister(&pfdev->base);
- pm_runtime_get_sync(pfdev->base.dev);
- pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->base.dev);
}
static ssize_t profiling_show(struct device *dev,
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init()
2026-08-28 20:56 ` [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
@ 2026-09-01 11:49 ` Boris Brezillon
2026-09-02 15:38 ` Adrián Larumbe
0 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 11:49 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:42 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Ideally the probe() function will do as little as possible, and all device
> initialisation and registration should happen inside the panfrost device
> subsystem, just like it's done in Panthor. This also simplifies resource
> unwinding in the error path.
>
> Do the same thing for DRM driver remove, as in, sweep most of the action
> into panfrost_device_fini(), just like we did for device probe.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 33 ++++++++++++++++++++++
> drivers/gpu/drm/panfrost/panfrost_drv.c | 44 +-----------------------------
> 2 files changed, 34 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 05c40d5a20b5..d2d2830f11a7 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -8,6 +8,7 @@
> #include <linux/pm_domain.h>
> #include <linux/pm_runtime.h>
> #include <linux/regulator/consumer.h>
> +#include <drm/drm_drv.h>
>
> #include "panfrost_device.h"
> #include "panfrost_devfreq.h"
> @@ -216,6 +217,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> {
> int err;
>
> + pfdev->comp = of_device_get_match_data(pfdev->base.dev);
> + if (!pfdev->comp)
> + return -ENODEV;
> +
> + pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
> +
> + mutex_init(&pfdev->shrinker_lock);
> + INIT_LIST_HEAD(&pfdev->shrinker_list);
> +
> mutex_init(&pfdev->sched_lock);
> INIT_LIST_HEAD(&pfdev->as_lru_list);
>
> @@ -284,8 +294,25 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> if (err)
> goto out_perfcnt;
>
> + pm_runtime_set_active(pfdev->base.dev);
> + pm_runtime_mark_last_busy(pfdev->base.dev);
> + pm_runtime_enable(pfdev->base.dev);
> + pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
> + pm_runtime_use_autosuspend(pfdev->base.dev);
> +
> + /*
> + * Register the DRM device with the core and the connectors with
> + * sysfs
> + */
> + err = drm_dev_register(&pfdev->base, 0);
> + if (err < 0)
> + goto out_devreg;
> +
> return 0;
>
> +out_devreg:
Not a huge fan of labels that describe where this is jumped from instead
of what is done under the label (that gets particularly confusing when
you start multiple locations jumping to the same label). So I'd suggest
renaming that one err_disable_rpm.
> + pm_runtime_disable(pfdev->base.dev);
I think you need a pm_runtime_dont_use_autosuspend() call before
pm_runtime_disable().
> + panfrost_gem_fini(pfdev);
> out_perfcnt:
> panfrost_perfcnt_fini(pfdev);
> out_job:
> @@ -304,11 +331,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> panfrost_reset_fini(pfdev);
> out_pm_domain:
> panfrost_pm_domain_fini(pfdev);
> + pm_runtime_set_suspended(pfdev->base.dev);
Do we have a good reason for not flagging the device suspended just
after the pm_runtime_disable() call in the error path? I mean, sure
it's not truly suspended until the clks/regulators have been turned
off, but it also wasn't suspended the before the initial
pm_runtime_set_active() call, and I like the idea of undoing things in
reverse init order.
> return err;
> }
>
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> + pm_runtime_get_sync(pfdev->base.dev);
pm_runtime_dont_use_autosuspend();
I see it fixed in patch 9, just like a few other issues that are made
more apparent by this code motion change. I guess it's fine but it
confused me, so it might be worth mentioning in the commit message.
> + pm_runtime_disable(pfdev->base.dev);
> +
> panfrost_gem_fini(pfdev);
> panfrost_perfcnt_fini(pfdev);
> panfrost_jm_fini(pfdev);
> @@ -319,6 +350,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
> panfrost_clk_fini(pfdev);
> panfrost_reset_fini(pfdev);
> panfrost_pm_domain_fini(pfdev);
> +
> + pm_runtime_set_suspended(pfdev->base.dev);
Fixed in patch 9, but the RPM ref you acquire at the beginning of the
function is never returned, so you end up with an unbalanced get/put.
This is a pre-existing issue, I know, this catches the eye of the
reviewer so we better mention that existing issues around PM are
preserved and will be fixed later.
> }
>
> #define PANFROST_EXCEPTION(id) \
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 9882a3ede75f..80996e311a9d 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> @@ -964,7 +964,6 @@ MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with T
> static int panfrost_probe(struct platform_device *pdev)
> {
> struct panfrost_device *pfdev;
> - int err;
>
> pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
> struct panfrost_device, base);
> @@ -973,45 +972,7 @@ static int panfrost_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, pfdev);
>
> - pfdev->comp = of_device_get_match_data(&pdev->dev);
> - if (!pfdev->comp)
> - return -ENODEV;
> -
> - pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
> -
> - mutex_init(&pfdev->shrinker_lock);
> - INIT_LIST_HEAD(&pfdev->shrinker_list);
> -
> - err = panfrost_device_init(pfdev);
> - if (err) {
> - if (err != -EPROBE_DEFER)
> - dev_err(&pdev->dev, "Fatal error during GPU init\n");
> - goto err_out0;
> - }
> -
> - pm_runtime_set_active(pfdev->base.dev);
> - pm_runtime_mark_last_busy(pfdev->base.dev);
> - pm_runtime_enable(pfdev->base.dev);
> - pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
> - pm_runtime_use_autosuspend(pfdev->base.dev);
> -
> - /*
> - * Register the DRM device with the core and the connectors with
> - * sysfs
> - */
> - err = drm_dev_register(&pfdev->base, 0);
> - if (err < 0)
> - goto err_out1;
> -
> -
> - return 0;
> -
> -err_out1:
> - pm_runtime_disable(pfdev->base.dev);
> - panfrost_device_fini(pfdev);
> - pm_runtime_set_suspended(pfdev->base.dev);
> -err_out0:
> - return err;
> + return panfrost_device_init(pfdev);
> }
>
> static void panfrost_remove(struct platform_device *pdev)
> @@ -1020,10 +981,7 @@ static void panfrost_remove(struct platform_device *pdev)
>
> drm_dev_unregister(&pfdev->base);
>
> - pm_runtime_get_sync(pfdev->base.dev);
> - pm_runtime_disable(pfdev->base.dev);
> panfrost_device_fini(pfdev);
> - pm_runtime_set_suspended(pfdev->base.dev);
> }
>
> static ssize_t profiling_show(struct device *dev,
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init()
2026-09-01 11:49 ` Boris Brezillon
@ 2026-09-02 15:38 ` Adrián Larumbe
2026-09-02 15:50 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:38 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 13:49, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:42 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > Ideally the probe() function will do as little as possible, and all device
> > initialisation and registration should happen inside the panfrost device
> > subsystem, just like it's done in Panthor. This also simplifies resource
> > unwinding in the error path.
> >
> > Do the same thing for DRM driver remove, as in, sweep most of the action
> > into panfrost_device_fini(), just like we did for device probe.
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 33 ++++++++++++++++++++++
> > drivers/gpu/drm/panfrost/panfrost_drv.c | 44 +-----------------------------
> > 2 files changed, 34 insertions(+), 43 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index 05c40d5a20b5..d2d2830f11a7 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > @@ -8,6 +8,7 @@
> > #include <linux/pm_domain.h>
> > #include <linux/pm_runtime.h>
> > #include <linux/regulator/consumer.h>
> > +#include <drm/drm_drv.h>
> >
> > #include "panfrost_device.h"
> > #include "panfrost_devfreq.h"
> > @@ -216,6 +217,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > {
> > int err;
> >
> > + pfdev->comp = of_device_get_match_data(pfdev->base.dev);
> > + if (!pfdev->comp)
> > + return -ENODEV;
> > +
> > + pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
> > +
> > + mutex_init(&pfdev->shrinker_lock);
> > + INIT_LIST_HEAD(&pfdev->shrinker_list);
> > +
> > mutex_init(&pfdev->sched_lock);
> > INIT_LIST_HEAD(&pfdev->as_lru_list);
> >
> > @@ -284,8 +294,25 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > if (err)
> > goto out_perfcnt;
> >
> > + pm_runtime_set_active(pfdev->base.dev);
> > + pm_runtime_mark_last_busy(pfdev->base.dev);
> > + pm_runtime_enable(pfdev->base.dev);
> > + pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
> > + pm_runtime_use_autosuspend(pfdev->base.dev);
> > +
> > + /*
> > + * Register the DRM device with the core and the connectors with
> > + * sysfs
> > + */
> > + err = drm_dev_register(&pfdev->base, 0);
> > + if (err < 0)
> > + goto out_devreg;
> > +
> > return 0;
> >
> > +out_devreg:
>
> Not a huge fan of labels that describe where this is jumped from instead
> of what is done under the label (that gets particularly confusing when
> you start multiple locations jumping to the same label). So I'd suggest
> renaming that one err_disable_rpm.
Will rename in the next revision.
> > + pm_runtime_disable(pfdev->base.dev);
>
> I think you need a pm_runtime_dont_use_autosuspend() call before
> pm_runtime_disable().
>
> > + panfrost_gem_fini(pfdev);
> > out_perfcnt:
> > panfrost_perfcnt_fini(pfdev);
> > out_job:
> > @@ -304,11 +331,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > panfrost_reset_fini(pfdev);
> > out_pm_domain:
> > panfrost_pm_domain_fini(pfdev);
> > + pm_runtime_set_suspended(pfdev->base.dev);
>
> Do we have a good reason for not flagging the device suspended just
> after the pm_runtime_disable() call in the error path? I mean, sure
> it's not truly suspended until the clks/regulators have been turned
> off, but it also wasn't suspended the before the initial
> pm_runtime_set_active() call, and I like the idea of undoing things in
> reverse init order.
That was my thought when I put it at the end, although since we manually
manage device power down here rather than going through the RPM runtime
suspend callback, I guess it makes no difference.
> but it also wasn't suspended the before the initial pm_runtime_set_active() call
I think devices begin their lifetime with power.runtime_status set to RPM_SUSPENDED:
drivers/base/power/runtime.c::pm_runtime_init
> > return err;
> > }
> >
> > void panfrost_device_fini(struct panfrost_device *pfdev)
> > {
> > + pm_runtime_get_sync(pfdev->base.dev);
>
> pm_runtime_dont_use_autosuspend();
>
> I see it fixed in patch 9, just like a few other issues that are made
> more apparent by this code motion change. I guess it's fine but it
> confused me, so it might be worth mentioning in the commit message.
>
>
> > + pm_runtime_disable(pfdev->base.dev);
> > +
> > panfrost_gem_fini(pfdev);
> > panfrost_perfcnt_fini(pfdev);
> > panfrost_jm_fini(pfdev);
> > @@ -319,6 +350,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
> > panfrost_clk_fini(pfdev);
> > panfrost_reset_fini(pfdev);
> > panfrost_pm_domain_fini(pfdev);
> > +
> > + pm_runtime_set_suspended(pfdev->base.dev);
>
> Fixed in patch 9, but the RPM ref you acquire at the beginning of the
> function is never returned, so you end up with an unbalanced get/put.
> This is a pre-existing issue, I know, this catches the eye of the
> reviewer so we better mention that existing issues around PM are
> preserved and will be fixed later.
Maybe I should apply those fixes first and then rebase this patch in top of them?
> > }
> >
> > #define PANFROST_EXCEPTION(id) \
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > index 9882a3ede75f..80996e311a9d 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > @@ -964,7 +964,6 @@ MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with T
> > static int panfrost_probe(struct platform_device *pdev)
> > {
> > struct panfrost_device *pfdev;
> > - int err;
> >
> > pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
> > struct panfrost_device, base);
> > @@ -973,45 +972,7 @@ static int panfrost_probe(struct platform_device *pdev)
> >
> > platform_set_drvdata(pdev, pfdev);
> >
> > - pfdev->comp = of_device_get_match_data(&pdev->dev);
> > - if (!pfdev->comp)
> > - return -ENODEV;
> > -
> > - pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
> > -
> > - mutex_init(&pfdev->shrinker_lock);
> > - INIT_LIST_HEAD(&pfdev->shrinker_list);
> > -
> > - err = panfrost_device_init(pfdev);
> > - if (err) {
> > - if (err != -EPROBE_DEFER)
> > - dev_err(&pdev->dev, "Fatal error during GPU init\n");
> > - goto err_out0;
> > - }
> > -
> > - pm_runtime_set_active(pfdev->base.dev);
> > - pm_runtime_mark_last_busy(pfdev->base.dev);
> > - pm_runtime_enable(pfdev->base.dev);
> > - pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
> > - pm_runtime_use_autosuspend(pfdev->base.dev);
> > -
> > - /*
> > - * Register the DRM device with the core and the connectors with
> > - * sysfs
> > - */
> > - err = drm_dev_register(&pfdev->base, 0);
> > - if (err < 0)
> > - goto err_out1;
> > -
> > -
> > - return 0;
> > -
> > -err_out1:
> > - pm_runtime_disable(pfdev->base.dev);
> > - panfrost_device_fini(pfdev);
> > - pm_runtime_set_suspended(pfdev->base.dev);
> > -err_out0:
> > - return err;
> > + return panfrost_device_init(pfdev);
> > }
> >
> > static void panfrost_remove(struct platform_device *pdev)
> > @@ -1020,10 +981,7 @@ static void panfrost_remove(struct platform_device *pdev)
> >
> > drm_dev_unregister(&pfdev->base);
> >
> > - pm_runtime_get_sync(pfdev->base.dev);
> > - pm_runtime_disable(pfdev->base.dev);
> > panfrost_device_fini(pfdev);
> > - pm_runtime_set_suspended(pfdev->base.dev);
> > }
> >
> > static ssize_t profiling_show(struct device *dev,
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init()
2026-09-02 15:38 ` Adrián Larumbe
@ 2026-09-02 15:50 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 15:50 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:38:25 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> > > + pm_runtime_disable(pfdev->base.dev);
> >
> > I think you need a pm_runtime_dont_use_autosuspend() call before
> > pm_runtime_disable().
> >
> > > + panfrost_gem_fini(pfdev);
> > > out_perfcnt:
> > > panfrost_perfcnt_fini(pfdev);
> > > out_job:
> > > @@ -304,11 +331,15 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > > panfrost_reset_fini(pfdev);
> > > out_pm_domain:
> > > panfrost_pm_domain_fini(pfdev);
> > > + pm_runtime_set_suspended(pfdev->base.dev);
> >
> > Do we have a good reason for not flagging the device suspended just
> > after the pm_runtime_disable() call in the error path? I mean, sure
> > it's not truly suspended until the clks/regulators have been turned
> > off, but it also wasn't suspended the before the initial
> > pm_runtime_set_active() call, and I like the idea of undoing things in
> > reverse init order.
>
> That was my thought when I put it at the end, although since we manually
> manage device power down here rather than going through the RPM runtime
> suspend callback, I guess it makes no difference.
>
> > but it also wasn't suspended the before the initial pm_runtime_set_active() call
>
> I think devices begin their lifetime with power.runtime_status set to RPM_SUSPENDED:
> drivers/base/power/runtime.c::pm_runtime_init
Yes, the SW state is RPM_SUSPENDED at probe time, but my point is that
the device is actually active before pm_runtime_set_active() is called,
so the disconnect between the SW state and the HW state already exists
and is accepted. I think, as long as we manipulate the state while RPM
is disabled, and we make sure to restore the resources into their
suspended-state before leaving, it doesn't matter when exactly we call
pm_runtime_set_suspended(). If my understanding is correct, I'd rather
have this call just after the pm_runtime_disable() to respect the
reversed-init-order convention we have in error paths.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 01/17] drm/panfrost: Move shrinker initialization and unplug one level down Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 02/17] drm/panfrost: Move all DRM device initialisation into device_init() Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:14 ` sashiko-bot
2026-09-01 12:10 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
` (13 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The device init function feels a bit cluttered with things that should be
done in the specific subsystems where those locks and lists are being
used. Same goes for module parameters, it's best to limit them to the
subsystems where they're relevant.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 10 ----------
drivers/gpu/drm/panfrost/panfrost_drv.c | 7 -------
drivers/gpu/drm/panfrost/panfrost_drv.h | 13 -------------
drivers/gpu/drm/panfrost/panfrost_gem.c | 9 ++++++++-
drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 3 +++
drivers/gpu/drm/panfrost/panfrost_gpu.c | 2 ++
drivers/gpu/drm/panfrost/panfrost_job.c | 1 +
drivers/gpu/drm/panfrost/panfrost_mmu.c | 3 +++
8 files changed, 17 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index d2d2830f11a7..7daa2143f3c4 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -223,16 +223,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
- mutex_init(&pfdev->sched_lock);
- INIT_LIST_HEAD(&pfdev->as_lru_list);
-
- spin_lock_init(&pfdev->as_lock);
-
- spin_lock_init(&pfdev->cycle_counter.lock);
-
#ifdef CONFIG_DEBUG_FS
mutex_init(&pfdev->debugfs.gems_lock);
INIT_LIST_HEAD(&pfdev->debugfs.gems_list);
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 80996e311a9d..ec8c1c08e147 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -21,7 +21,6 @@
#include <drm/drm_utils.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
#include "panfrost_job.h"
@@ -955,12 +954,6 @@ static const struct drm_driver panfrost_drm_driver = {
#endif
};
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-bool panfrost_transparent_hugepage = true;
-module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
-MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
-#endif
-
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
deleted file mode 100644
index bac0c415d4da..000000000000
--- a/drivers/gpu/drm/panfrost/panfrost_drv.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 or MIT */
-/* Copyright 2025 Amazon.com, Inc. or its affiliates */
-
-#ifndef __PANFROST_DRV_H__
-#define __PANFROST_DRV_H__
-
-#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
-extern bool panfrost_transparent_hugepage;
-#else
-#define panfrost_transparent_hugepage false
-#endif
-
-#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 7b993a089af2..15105b7f954b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -11,10 +11,17 @@
#include <drm/panfrost_drm.h>
#include <drm/drm_print.h>
#include "panfrost_device.h"
-#include "panfrost_drv.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+bool panfrost_transparent_hugepage = true;
+module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
+MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
+#else
+#define panfrost_transparent_hugepage false
+#endif
+
int panfrost_gem_init(struct panfrost_device *pfdev)
{
int err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index fefae87535d6..72cd7a165e6d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -97,6 +97,9 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*/
int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
{
+ mutex_init(&pfdev->shrinker_lock);
+ INIT_LIST_HEAD(&pfdev->shrinker_list);
+
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
return -ENOMEM;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 7d555e63e21a..8a15ccce08e9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -516,6 +516,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
{
int err;
+ spin_lock_init(&pfdev->cycle_counter.lock);
+
err = panfrost_gpu_soft_reset(pfdev);
if (err)
return err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 2d12b83e900a..6f920fd0fc4e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -873,6 +873,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
+ mutex_init(&pfdev->sched_lock);
js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
if (js->irq < 0)
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 4a3162c3b659..5c393ed6e310 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -955,6 +955,9 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
{
int err;
+ INIT_LIST_HEAD(&pfdev->as_lru_list);
+ spin_lock_init(&pfdev->as_lock);
+
pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
if (pfdev->mmu_irq < 0)
return pfdev->mmu_irq;
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems
2026-08-28 20:56 ` [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
@ 2026-08-28 21:14 ` sashiko-bot
2026-09-01 12:10 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:14 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:
- [Low] Global variable lacks `static` qualifier after being localized to a single file
--
commit 8f093246201046313865b72d1d462aeb11847f4f
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Move lock and modparam initialisations into their subsystems
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.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> index 7b993a089af2d..15105b7f954bf 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
[ ... ]
> @@ -11,10 +11,17 @@
> #include <drm/panfrost_drm.h>
> #include <drm/drm_print.h>
> #include "panfrost_device.h"
> -#include "panfrost_drv.h"
> #include "panfrost_gem.h"
> #include "panfrost_mmu.h"
>
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +bool panfrost_transparent_hugepage = true;
[Severity: Low]
Since panfrost_transparent_hugepage was moved into panfrost_gem.c and its
declaration was removed from panfrost_drv.h, should this be declared as static?
Without the static qualifier, this variable is now exposed to the global
namespace and triggers a Sparse warning ("symbol
'panfrost_transparent_hugepage' was not declared. Should it be static?").
> +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)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=3
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems
2026-08-28 20:56 ` [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
2026-08-28 21:14 ` sashiko-bot
@ 2026-09-01 12:10 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 12:10 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:43 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> The device init function feels a bit cluttered with things that should be
> done in the specific subsystems where those locks and lists are being
> used. Same goes for module parameters, it's best to limit them to the
> subsystems where they're relevant.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
It would also be good to have opaque structs for sub-components instead
stuffing everything in panthor_device, but that can wait.
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 10 ----------
> drivers/gpu/drm/panfrost/panfrost_drv.c | 7 -------
> drivers/gpu/drm/panfrost/panfrost_drv.h | 13 -------------
> drivers/gpu/drm/panfrost/panfrost_gem.c | 9 ++++++++-
> drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 3 +++
> drivers/gpu/drm/panfrost/panfrost_gpu.c | 2 ++
> drivers/gpu/drm/panfrost/panfrost_job.c | 1 +
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 3 +++
> 8 files changed, 17 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index d2d2830f11a7..7daa2143f3c4 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -223,16 +223,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT;
>
> - mutex_init(&pfdev->shrinker_lock);
> - INIT_LIST_HEAD(&pfdev->shrinker_list);
> -
> - mutex_init(&pfdev->sched_lock);
> - INIT_LIST_HEAD(&pfdev->as_lru_list);
> -
> - spin_lock_init(&pfdev->as_lock);
> -
> - spin_lock_init(&pfdev->cycle_counter.lock);
> -
> #ifdef CONFIG_DEBUG_FS
> mutex_init(&pfdev->debugfs.gems_lock);
> INIT_LIST_HEAD(&pfdev->debugfs.gems_list);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 80996e311a9d..ec8c1c08e147 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> @@ -21,7 +21,6 @@
> #include <drm/drm_utils.h>
>
> #include "panfrost_device.h"
> -#include "panfrost_drv.h"
> #include "panfrost_gem.h"
> #include "panfrost_mmu.h"
> #include "panfrost_job.h"
> @@ -955,12 +954,6 @@ static const struct drm_driver panfrost_drm_driver = {
> #endif
> };
>
> -#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> -bool panfrost_transparent_hugepage = true;
> -module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
> -MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
> -#endif
> -
> static int panfrost_probe(struct platform_device *pdev)
> {
> struct panfrost_device *pfdev;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h
> deleted file mode 100644
> index bac0c415d4da..000000000000
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.h
> +++ /dev/null
> @@ -1,13 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0 or MIT */
> -/* Copyright 2025 Amazon.com, Inc. or its affiliates */
> -
> -#ifndef __PANFROST_DRV_H__
> -#define __PANFROST_DRV_H__
> -
> -#if IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)
> -extern bool panfrost_transparent_hugepage;
> -#else
> -#define panfrost_transparent_hugepage false
> -#endif
> -
> -#endif
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> index 7b993a089af2..15105b7f954b 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> @@ -11,10 +11,17 @@
> #include <drm/panfrost_drm.h>
> #include <drm/drm_print.h>
> #include "panfrost_device.h"
> -#include "panfrost_drv.h"
> #include "panfrost_gem.h"
> #include "panfrost_mmu.h"
>
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +bool panfrost_transparent_hugepage = true;
> +module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400);
> +MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)");
> +#else
> +#define panfrost_transparent_hugepage false
> +#endif
> +
> int panfrost_gem_init(struct panfrost_device *pfdev)
> {
> int err;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> index fefae87535d6..72cd7a165e6d 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> @@ -97,6 +97,9 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
> */
> int panfrost_gem_shrinker_init(struct panfrost_device *pfdev)
> {
> + mutex_init(&pfdev->shrinker_lock);
> + INIT_LIST_HEAD(&pfdev->shrinker_list);
> +
> pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
> if (!pfdev->shrinker)
> return -ENOMEM;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> index 7d555e63e21a..8a15ccce08e9 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> @@ -516,6 +516,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
> {
> int err;
>
> + spin_lock_init(&pfdev->cycle_counter.lock);
> +
> err = panfrost_gpu_soft_reset(pfdev);
> if (err)
> return err;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index 2d12b83e900a..6f920fd0fc4e 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
> @@ -873,6 +873,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
>
> INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
> spin_lock_init(&js->job_lock);
> + mutex_init(&pfdev->sched_lock);
>
> js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
> if (js->irq < 0)
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> index 4a3162c3b659..5c393ed6e310 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> @@ -955,6 +955,9 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
> {
> int err;
>
> + INIT_LIST_HEAD(&pfdev->as_lru_list);
> + spin_lock_init(&pfdev->as_lock);
> +
> pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
> if (pfdev->mmu_irq < 0)
> return pfdev->mmu_irq;
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (2 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 03/17] drm/panfrost: Move lock and modparam initialisations into their subsystems Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 12:30 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 05/17] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
` (12 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Because debugfs knobs are a device interface, and also the different knobs
deal with specific subsystems, it's best to move their initialisations away
from panfrost_drv.c and into the subsystem they provide information about.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 8 ++
drivers/gpu/drm/panfrost/panfrost_device.h | 4 +
drivers/gpu/drm/panfrost/panfrost_drv.c | 129 +----------------------------
drivers/gpu/drm/panfrost/panfrost_gem.c | 29 ++++++-
drivers/gpu/drm/panfrost/panfrost_gem.h | 3 +-
drivers/gpu/drm/panfrost/panfrost_job.c | 99 ++++++++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_job.h | 4 +
7 files changed, 144 insertions(+), 132 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 7daa2143f3c4..6e3fcd53b690 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -591,3 +591,11 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
RUNTIME_PM_OPS(panfrost_device_runtime_suspend, panfrost_device_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(panfrost_device_suspend, panfrost_device_resume)
};
+
+#ifdef CONFIG_DEBUG_FS
+void panfrost_device_debugfs_init(struct drm_minor *minor)
+{
+ panfrost_gems_debugfs_init(minor);
+ panfrost_sched_debugfs_init(minor);
+}
+#endif // CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 1fe1554f2652..a0b9a2145fc9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -341,4 +341,8 @@ panfrost_device_schedule_reset(struct panfrost_device *pfdev)
queue_work(pfdev->reset.wq, &pfdev->reset.work);
}
+#ifdef CONFIG_DEBUG_FS
+void panfrost_device_debugfs_init(struct drm_minor *minor);
+#endif // CONFIG_DEBUG_FS
+
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index ec8c1c08e147..f77780c72a1a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -792,133 +792,6 @@ static const struct file_operations panfrost_drm_driver_fops = {
.show_fdinfo = drm_show_fdinfo,
};
-#ifdef CONFIG_DEBUG_FS
-static int panthor_gems_show(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = m->private;
- struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
-
- panfrost_gem_debugfs_print_bos(pfdev, m);
-
- return 0;
-}
-
-static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
- struct seq_file *m)
-{
- struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
- const char *prio = "UNKNOWN";
-
- static const char * const prios[] = {
- [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
- [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
- [DRM_SCHED_PRIORITY_LOW] = "LOW",
- };
-
- if (jm_ctx->slot_entity[0].priority !=
- jm_ctx->slot_entity[1].priority)
- drm_warn(ddev, "Slot priorities should be the same in a single context");
-
- if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
- prio = prios[jm_ctx->slot_entity[0].priority];
-
- seq_printf(m, " JM context %u: priority %s\n", handle, prio);
-}
-
-static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
- struct seq_file *m)
-{
- struct panfrost_jm_ctx *jm_ctx;
- unsigned long i;
-
- xa_lock(&pfile->jm_ctxs);
- xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
- jm_ctx = panfrost_jm_ctx_get(jm_ctx);
- xa_unlock(&pfile->jm_ctxs);
- show_panfrost_jm_ctx(jm_ctx, i, m);
- panfrost_jm_ctx_put(jm_ctx);
- xa_lock(&pfile->jm_ctxs);
- }
- xa_unlock(&pfile->jm_ctxs);
-
- return 0;
-}
-
-static struct drm_info_list panthor_debugfs_list[] = {
- {"gems",
- panthor_gems_show, 0, NULL},
-};
-
-static int panthor_gems_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panthor_debugfs_list,
- ARRAY_SIZE(panthor_debugfs_list),
- minor->debugfs_root, minor);
-
- return 0;
-}
-
-static int show_each_file(struct seq_file *m, void *arg)
-{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *ddev = node->minor->dev;
- int (*show)(struct panfrost_file_priv *, struct seq_file *) =
- node->info_ent->data;
- struct drm_file *file;
- int ret;
-
- ret = mutex_lock_interruptible(&ddev->filelist_mutex);
- if (ret)
- return ret;
-
- list_for_each_entry(file, &ddev->filelist, lhead) {
- struct task_struct *task;
- struct panfrost_file_priv *pfile = file->driver_priv;
- struct pid *pid;
-
- /*
- * Although we have a valid reference on file->pid, that does
- * not guarantee that the task_struct who called get_pid() is
- * still alive (e.g. get_pid(current) => fork() => exit()).
- * Therefore, we need to protect this ->comm access using RCU.
- */
- rcu_read_lock();
- pid = rcu_dereference(file->pid);
- task = pid_task(pid, PIDTYPE_TGID);
- seq_printf(m, "client_id %8llu pid %8d command %s:\n",
- file->client_id, pid_nr(pid),
- task ? task->comm : "<unknown>");
- rcu_read_unlock();
-
- ret = show(pfile, m);
- if (ret < 0)
- break;
-
- seq_puts(m, "\n");
- }
-
- mutex_unlock(&ddev->filelist_mutex);
- return ret;
-}
-
-static struct drm_info_list panfrost_sched_debugfs_list[] = {
- { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
-};
-
-static void panfrost_sched_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panfrost_sched_debugfs_list,
- ARRAY_SIZE(panfrost_sched_debugfs_list),
- minor->debugfs_root, minor);
-}
-
-static void panfrost_debugfs_init(struct drm_minor *minor)
-{
- panthor_gems_debugfs_init(minor);
- panfrost_sched_debugfs_init(minor);
-}
-#endif
-
/*
* Panfrost driver version:
* - 1.0 - initial interface
@@ -950,7 +823,7 @@ static const struct drm_driver panfrost_drm_driver = {
.gem_prime_import = panfrost_gem_prime_import,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
#ifdef CONFIG_DEBUG_FS
- .debugfs_init = panfrost_debugfs_init,
+ .debugfs_init = panfrost_device_debugfs_init,
#endif
};
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 15105b7f954b..54717fdb8624 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -9,6 +9,7 @@
#include <linux/dma-mapping.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_debugfs.h>
#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_gem.h"
@@ -736,8 +737,8 @@ static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo,
totals->reclaimable += resident_size;
}
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m)
+static void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
+ struct seq_file *m)
{
struct gem_size_totals totals = {0};
struct panfrost_gem_object *bo;
@@ -757,4 +758,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
totals.size, totals.resident, totals.reclaimable);
}
+
+static int panfrost_gems_show(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
+
+ panfrost_gem_debugfs_print_bos(pfdev, m);
+
+ return 0;
+}
+
+static struct drm_info_list panfrost_debugfs_list[] = {
+ {"gems",
+ panfrost_gems_show, 0, NULL},
+};
+
+int panfrost_gems_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_debugfs_list,
+ ARRAY_SIZE(panfrost_debugfs_list),
+ minor->debugfs_root, minor);
+
+ return 0;
+}
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 5c823cdbd980..49bb5691087e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -164,8 +164,7 @@ int panfrost_gem_sync(struct drm_gem_object *obj, u32 type,
void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label);
#ifdef CONFIG_DEBUG_FS
-void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
- struct seq_file *m);
+int panfrost_gems_debugfs_init(struct drm_minor *minor);
#endif
#endif /* __PANFROST_GEM_H__ */
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 6f920fd0fc4e..5016d2d53da2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -8,6 +8,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-resv.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <drm/panfrost_drm.h>
@@ -1137,3 +1139,100 @@ int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle)
panfrost_jm_ctx_put(jm_ctx);
return 0;
}
+
+#ifdef CONFIG_DEBUG_FS
+static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
+ struct seq_file *m)
+{
+ struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
+ const char *prio = "UNKNOWN";
+
+ static const char * const prios[] = {
+ [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
+ [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
+ [DRM_SCHED_PRIORITY_LOW] = "LOW",
+ };
+
+ if (jm_ctx->slot_entity[0].priority !=
+ jm_ctx->slot_entity[1].priority)
+ drm_warn(ddev, "Slot priorities should be the same in a single context");
+
+ if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
+ prio = prios[jm_ctx->slot_entity[0].priority];
+
+ seq_printf(m, " JM context %u: priority %s\n", handle, prio);
+}
+
+static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
+ struct seq_file *m)
+{
+ struct panfrost_jm_ctx *jm_ctx;
+ unsigned long i;
+
+ xa_lock(&pfile->jm_ctxs);
+ xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
+ jm_ctx = panfrost_jm_ctx_get(jm_ctx);
+ xa_unlock(&pfile->jm_ctxs);
+ show_panfrost_jm_ctx(jm_ctx, i, m);
+ panfrost_jm_ctx_put(jm_ctx);
+ xa_lock(&pfile->jm_ctxs);
+ }
+ xa_unlock(&pfile->jm_ctxs);
+
+ return 0;
+}
+
+static int show_each_file(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *ddev = node->minor->dev;
+ int (*show)(struct panfrost_file_priv *, struct seq_file *) =
+ node->info_ent->data;
+ struct drm_file *file;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ddev->filelist_mutex);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(file, &ddev->filelist, lhead) {
+ struct task_struct *task;
+ struct panfrost_file_priv *pfile = file->driver_priv;
+ struct pid *pid;
+
+ /*
+ * Although we have a valid reference on file->pid, that does
+ * not guarantee that the task_struct who called get_pid() is
+ * still alive (e.g. get_pid(current) => fork() => exit()).
+ * Therefore, we need to protect this ->comm access using RCU.
+ */
+ rcu_read_lock();
+ pid = rcu_dereference(file->pid);
+ task = pid_task(pid, PIDTYPE_TGID);
+ seq_printf(m, "client_id %8llu pid %8d command %s:\n",
+ file->client_id, pid_nr(pid),
+ task ? task->comm : "<unknown>");
+ rcu_read_unlock();
+
+ ret = show(pfile, m);
+ if (ret < 0)
+ break;
+
+ seq_puts(m, "\n");
+ }
+
+ mutex_unlock(&ddev->filelist_mutex);
+ return ret;
+}
+
+static struct drm_info_list panfrost_sched_debugfs_list[] = {
+ { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
+};
+
+void panfrost_sched_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_sched_debugfs_list,
+ ARRAY_SIZE(panfrost_sched_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index c3f57e41a571..3e3b9717838f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -74,4 +74,8 @@ int panfrost_job_get_slot(struct panfrost_job *job);
int panfrost_job_push(struct panfrost_job *job);
void panfrost_job_put(struct panfrost_job *job);
+#ifdef CONFIG_DEBUG_FS
+void panfrost_sched_debugfs_init(struct drm_minor *minor);
+#endif
+
#endif
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems
2026-08-28 20:56 ` [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
@ 2026-09-01 12:30 ` Boris Brezillon
2026-09-02 15:40 ` Adrián Larumbe
0 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 12:30 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:44 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> @@ -757,4 +758,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
> seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
> totals.size, totals.resident, totals.reclaimable);
> }
> +
> +static int panfrost_gems_show(struct seq_file *m, void *data)
> +{
> + struct drm_info_node *node = m->private;
> + struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
> +
> + panfrost_gem_debugfs_print_bos(pfdev, m);
> +
> + return 0;
> +}
> +
> +static struct drm_info_list panfrost_debugfs_list[] = {
> + {"gems",
> + panfrost_gems_show, 0, NULL},
> +};
> +
> +int panfrost_gems_debugfs_init(struct drm_minor *minor)
nit: I would rename that one panfrost_gem_debugfs_init().
The rest looks good
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> +{
> + drm_debugfs_create_files(panfrost_debugfs_list,
> + ARRAY_SIZE(panfrost_debugfs_list),
> + minor->debugfs_root, minor);
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems
2026-09-01 12:30 ` Boris Brezillon
@ 2026-09-02 15:40 ` Adrián Larumbe
0 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:40 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 14:30, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:44 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > @@ -757,4 +758,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev,
> > seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
> > totals.size, totals.resident, totals.reclaimable);
> > }
> > +
> > +static int panfrost_gems_show(struct seq_file *m, void *data)
> > +{
> > + struct drm_info_node *node = m->private;
> > + struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
> > +
> > + panfrost_gem_debugfs_print_bos(pfdev, m);
> > +
> > + return 0;
> > +}
> > +
> > +static struct drm_info_list panfrost_debugfs_list[] = {
> > + {"gems",
> > + panfrost_gems_show, 0, NULL},
> > +};
> > +
> > +int panfrost_gems_debugfs_init(struct drm_minor *minor)
>
>
> nit: I would rename that one panfrost_gem_debugfs_init().
Will do.
> The rest looks good
>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
>
> > +{
> > + drm_debugfs_create_files(panfrost_debugfs_list,
> > + ARRAY_SIZE(panfrost_debugfs_list),
> > + minor->debugfs_root, minor);
> > +
> > + return 0;
> > +}
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 05/17] drm/panfrost: Skip NULL checks for clock enable/disabling
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (3 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 04/17] drm/panfrost: Move debugfs initialisation to relevant subsystems Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 12:31 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
` (11 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The clk_* API can deal with NULL clock objects, so we can do away with NULL
checks in client code.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 6e3fcd53b690..a9d9669912e0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -540,11 +540,9 @@ static int panfrost_device_resume(struct device *dev)
if (ret)
goto err_clk;
- if (pfdev->bus_clock) {
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
- }
+ ret = clk_enable(pfdev->bus_clock);
+ if (ret)
+ goto err_bus_clk;
}
ret = pm_runtime_force_resume(dev);
@@ -554,7 +552,7 @@ static int panfrost_device_resume(struct device *dev)
return 0;
err_resume:
- if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock)
+ if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
clk_disable(pfdev->bus_clock);
err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
@@ -575,9 +573,7 @@ static int panfrost_device_suspend(struct device *dev)
return ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- if (pfdev->bus_clock)
- clk_disable(pfdev->bus_clock);
-
+ clk_disable(pfdev->bus_clock);
clk_disable(pfdev->clock);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 05/17] drm/panfrost: Skip NULL checks for clock enable/disabling
2026-08-28 20:56 ` [PATCH v7 05/17] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
@ 2026-09-01 12:31 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 12:31 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:45 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> The clk_* API can deal with NULL clock objects, so we can do away with NULL
> checks in client code.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 14 +++++---------
> 1 file changed, 5 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 6e3fcd53b690..a9d9669912e0 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -540,11 +540,9 @@ static int panfrost_device_resume(struct device *dev)
> if (ret)
> goto err_clk;
>
> - if (pfdev->bus_clock) {
> - ret = clk_enable(pfdev->bus_clock);
> - if (ret)
> - goto err_bus_clk;
> - }
> + ret = clk_enable(pfdev->bus_clock);
> + if (ret)
> + goto err_bus_clk;
> }
>
> ret = pm_runtime_force_resume(dev);
> @@ -554,7 +552,7 @@ static int panfrost_device_resume(struct device *dev)
> return 0;
>
> err_resume:
> - if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock)
> + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
> clk_disable(pfdev->bus_clock);
> err_bus_clk:
> if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
> @@ -575,9 +573,7 @@ static int panfrost_device_suspend(struct device *dev)
> return ret;
>
> if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
> - if (pfdev->bus_clock)
> - clk_disable(pfdev->bus_clock);
> -
> + clk_disable(pfdev->bus_clock);
> clk_disable(pfdev->clock);
> }
>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (4 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 05/17] drm/panfrost: Skip NULL checks for clock enable/disabling Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 12:38 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown Adrián Larumbe
` (10 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Gather all clock enables and disables into a single function to avoid
repetition. Also, as part of the same function, handle reset control
(de)assertions, since that was already the case in the PM runtime
functions.
Also do clk (un)prepares and dis/enables at the same time, since the
clk_prepare_* family of functions can simply increase the refcnt of
an already prepared clock.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 123 +++++++++++++----------------
1 file changed, 53 insertions(+), 70 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index a9d9669912e0..9549ddb7854a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -27,18 +27,53 @@ static int panfrost_reset_init(struct panfrost_device *pfdev)
return PTR_ERR(pfdev->rstc);
}
- return reset_control_deassert(pfdev->rstc);
+ return 0;
+}
+
+static int panfrost_clk_enable_deassert_reset(struct panfrost_device *pfdev)
+{
+ int err;
+
+ err = reset_control_deassert(pfdev->rstc);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(pfdev->clock);
+ if (err)
+ goto assert_reset;
+
+ err = clk_prepare_enable(pfdev->bus_clock);
+ if (err)
+ goto disable_clock;
+
+ err = clk_prepare_enable(pfdev->bus_ace_clock);
+ if (err)
+ goto disable_bus_clock;
+
+ return 0;
+
+disable_bus_clock:
+ clk_disable_unprepare(pfdev->bus_clock);
+disable_clock:
+ clk_disable_unprepare(pfdev->clock);
+assert_reset:
+ reset_control_assert(pfdev->rstc);
+
+ return err;
}
-static void panfrost_reset_fini(struct panfrost_device *pfdev)
+static void panfrost_clk_disable_assert_reset(struct panfrost_device *pfdev)
{
+ clk_disable_unprepare(pfdev->bus_ace_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
reset_control_assert(pfdev->rstc);
}
static int panfrost_clk_init(struct panfrost_device *pfdev)
{
- int err;
unsigned long rate;
+ int err = 0;
pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
if (IS_ERR(pfdev->clock)) {
@@ -49,53 +84,31 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
rate = clk_get_rate(pfdev->clock);
dev_info(pfdev->base.dev, "clock rate = %lu\n", rate);
- err = clk_prepare_enable(pfdev->clock);
- if (err)
- return err;
-
pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus");
if (IS_ERR(pfdev->bus_clock)) {
- dev_err(pfdev->base.dev, "get bus_clock failed %ld\n",
- PTR_ERR(pfdev->bus_clock));
err = PTR_ERR(pfdev->bus_clock);
- goto disable_clock;
+ dev_err(pfdev->base.dev, "get bus_clock failed %d\n", err);
+ return err;
}
if (pfdev->bus_clock) {
rate = clk_get_rate(pfdev->bus_clock);
dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate);
-
- err = clk_prepare_enable(pfdev->bus_clock);
- if (err)
- goto disable_clock;
}
pfdev->bus_ace_clock = devm_clk_get_optional(pfdev->base.dev, "bus_ace");
if (IS_ERR(pfdev->bus_ace_clock)) {
err = PTR_ERR(pfdev->bus_ace_clock);
dev_err(pfdev->base.dev, "get bus_ace_clock failed %d\n", err);
- goto disable_bus_clock;
+ return err;
}
- err = clk_prepare_enable(pfdev->bus_ace_clock);
- if (err)
- goto disable_bus_clock;
-
- return 0;
-
-disable_bus_clock:
- clk_disable_unprepare(pfdev->bus_clock);
-disable_clock:
- clk_disable_unprepare(pfdev->clock);
-
- return err;
+ return panfrost_clk_enable_deassert_reset(pfdev);
}
static void panfrost_clk_fini(struct panfrost_device *pfdev)
{
- clk_disable_unprepare(pfdev->bus_ace_clock);
- clk_disable_unprepare(pfdev->bus_clock);
- clk_disable_unprepare(pfdev->clock);
+ panfrost_clk_disable_assert_reset(pfdev);
}
static int panfrost_regulator_init(struct panfrost_device *pfdev)
@@ -241,7 +254,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
err = panfrost_clk_init(pfdev);
if (err) {
dev_err(pfdev->base.dev, "clk init failed %d\n", err);
- goto out_reset;
+ goto out_pm_domain;
}
err = panfrost_devfreq_init(pfdev);
@@ -317,8 +330,6 @@ int panfrost_device_init(struct panfrost_device *pfdev)
panfrost_devfreq_fini(pfdev);
out_clk:
panfrost_clk_fini(pfdev);
-out_reset:
- panfrost_reset_fini(pfdev);
out_pm_domain:
panfrost_pm_domain_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
@@ -338,7 +349,6 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_devfreq_fini(pfdev);
panfrost_regulator_fini(pfdev);
panfrost_clk_fini(pfdev);
- panfrost_reset_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
pm_runtime_set_suspended(pfdev->base.dev);
@@ -462,38 +472,15 @@ static int panfrost_device_runtime_resume(struct device *dev)
int ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- ret = reset_control_deassert(pfdev->rstc);
+ ret = panfrost_clk_enable_deassert_reset(pfdev);
if (ret)
return ret;
-
- ret = clk_enable(pfdev->clock);
- if (ret)
- goto err_clk;
-
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
-
- ret = clk_enable(pfdev->bus_ace_clock);
- if (ret)
- goto err_bus_ace_clk;
}
panfrost_device_reset(pfdev, true);
panfrost_devfreq_resume(pfdev);
return 0;
-
-err_bus_ace_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->bus_clock);
-err_bus_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- clk_disable(pfdev->clock);
-err_clk:
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
- reset_control_assert(pfdev->rstc);
- return ret;
}
static int panfrost_device_runtime_suspend(struct device *dev)
@@ -509,12 +496,8 @@ static int panfrost_device_runtime_suspend(struct device *dev)
panfrost_gpu_suspend_irq(pfdev);
panfrost_gpu_power_off(pfdev);
- if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- clk_disable(pfdev->bus_ace_clock);
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
- reset_control_assert(pfdev->rstc);
- }
+ if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
+ panfrost_clk_disable_assert_reset(pfdev);
return 0;
}
@@ -536,11 +519,11 @@ static int panfrost_device_resume(struct device *dev)
}
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- ret = clk_enable(pfdev->clock);
+ ret = clk_prepare_enable(pfdev->clock);
if (ret)
goto err_clk;
- ret = clk_enable(pfdev->bus_clock);
+ ret = clk_prepare_enable(pfdev->bus_clock);
if (ret)
goto err_bus_clk;
}
@@ -553,10 +536,10 @@ static int panfrost_device_resume(struct device *dev)
err_resume:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->bus_clock);
err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS))
- clk_disable(pfdev->clock);
+ clk_disable_unprepare(pfdev->clock);
err_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
dev_pm_opp_set_opp(dev, NULL);
@@ -573,8 +556,8 @@ static int panfrost_device_suspend(struct device *dev)
return ret;
if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) {
- clk_disable(pfdev->bus_clock);
- clk_disable(pfdev->clock);
+ clk_disable_unprepare(pfdev->bus_clock);
+ clk_disable_unprepare(pfdev->clock);
}
if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF))
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset
2026-08-28 20:56 ` [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
@ 2026-09-01 12:38 ` Boris Brezillon
2026-09-02 15:41 ` Adrián Larumbe
0 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 12:38 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:46 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Gather all clock enables and disables into a single function to avoid
> repetition. Also, as part of the same function, handle reset control
> (de)assertions, since that was already the case in the PM runtime
> functions.
I think I'd prefer to not mix clks and reset-lines. So basically, a
set of helpers that deals with clks (panfrost_clks_{enable,disable}()),
and manual calls for the reset line.
>
> Also do clk (un)prepares and dis/enables at the same time, since the
> clk_prepare_* family of functions can simply increase the refcnt of
> an already prepared clock.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset
2026-09-01 12:38 ` Boris Brezillon
@ 2026-09-02 15:41 ` Adrián Larumbe
0 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:41 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 14:38, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:46 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > Gather all clock enables and disables into a single function to avoid
> > repetition. Also, as part of the same function, handle reset control
> > (de)assertions, since that was already the case in the PM runtime
> > functions.
>
> I think I'd prefer to not mix clks and reset-lines. So basically, a
> set of helpers that deals with clks (panfrost_clks_{enable,disable}()),
> and manual calls for the reset line.
Acked, will do so in the next revision.
> >
> > Also do clk (un)prepares and dis/enables at the same time, since the
> > clk_prepare_* family of functions can simply increase the refcnt of
> > an already prepared clock.
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (5 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 06/17] drm/panfrost: Consolidate device clock management and reset Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:16 ` sashiko-bot
2026-09-01 12:58 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
` (9 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
We should make sure there are no inflight jobs at the time we finish driver
structures and synchronise their IRQs, and even more so by the time clocks
and power domains are disabled.
To that end, stop submissions from existing scheduler entities and force HW
to stop all running jobs.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 2 +
drivers/gpu/drm/panfrost/panfrost_job.c | 71 ++++++++++++++++++------------
drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
3 files changed, 46 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 9549ddb7854a..9e02fb5f73c8 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -341,6 +341,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
pm_runtime_get_sync(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
+ panfrost_jm_stop_sched_jobs(pfdev);
+
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
panfrost_jm_fini(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 5016d2d53da2..630298b7ea8a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -655,39 +655,12 @@ static u32 panfrost_active_slots(struct panfrost_device *pfdev,
return js_state & *js_state_mask;
}
-static void
-panfrost_reset(struct panfrost_device *pfdev,
- struct drm_sched_job *bad)
+static void panfrost_stop_jobs(struct panfrost_device *pfdev)
{
u32 js_state, js_state_mask = 0xffffffff;
unsigned int i, j;
- bool cookie;
int ret;
- if (!atomic_read(&pfdev->reset.pending))
- return;
-
- /* Stop the schedulers.
- *
- * FIXME: We temporarily get out of the dma_fence_signalling section
- * because the cleanup path generate lockdep splats when taking locks
- * to release job resources. We should rework the code to follow this
- * pattern:
- *
- * try_lock
- * if (locked)
- * release
- * else
- * schedule_work_to_release_later
- */
- for (i = 0; i < NUM_JOB_SLOTS; i++)
- drm_sched_stop(&pfdev->js->queue[i].sched, bad);
-
- cookie = dma_fence_begin_signalling();
-
- if (bad)
- drm_sched_increase_karma(bad);
-
/* Mask job interrupts and synchronize to make sure we won't be
* interrupted during our reset.
*/
@@ -730,6 +703,48 @@ panfrost_reset(struct panfrost_device *pfdev,
}
memset(pfdev->jobs, 0, sizeof(pfdev->jobs));
spin_unlock(&pfdev->js->job_lock);
+}
+
+void panfrost_jm_stop_sched_jobs(struct panfrost_device *pfdev)
+{
+ for (u32 i = 0; i < NUM_JOB_SLOTS; i++)
+ drm_sched_wqueue_stop(&pfdev->js->queue[i].sched);
+
+ panfrost_stop_jobs(pfdev);
+}
+
+static void
+panfrost_reset(struct panfrost_device *pfdev,
+ struct drm_sched_job *bad)
+{
+ unsigned int i;
+ bool cookie;
+
+ if (!atomic_read(&pfdev->reset.pending))
+ return;
+
+ /* Stop the schedulers.
+ *
+ * FIXME: We temporarily get out of the dma_fence_signalling section
+ * because the cleanup path generate lockdep splats when taking locks
+ * to release job resources. We should rework the code to follow this
+ * pattern:
+ *
+ * try_lock
+ * if (locked)
+ * release
+ * else
+ * schedule_work_to_release_later
+ */
+ for (i = 0; i < NUM_JOB_SLOTS; i++)
+ drm_sched_stop(&pfdev->js->queue[i].sched, bad);
+
+ cookie = dma_fence_begin_signalling();
+
+ if (bad)
+ drm_sched_increase_karma(bad);
+
+ panfrost_stop_jobs(pfdev);
/* Proceed with reset now. */
panfrost_device_reset(pfdev, false);
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index 3e3b9717838f..dc9d3c9dc67b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -73,6 +73,7 @@ int panfrost_jm_is_idle(struct panfrost_device *pfdev);
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);
+void panfrost_jm_stop_sched_jobs(struct panfrost_device *pfdev);
#ifdef CONFIG_DEBUG_FS
void panfrost_sched_debugfs_init(struct drm_minor *minor);
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown
2026-08-28 20:56 ` [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown Adrián Larumbe
@ 2026-08-28 21:16 ` sashiko-bot
2026-09-01 12:58 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:16 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Critical] panfrost_stop_jobs() unintentionally commands the hardware to restart pending jobs during device teardown, leading to GPU execution with freed memory and unmapped page tables.
- [High] Jobs aborted during device teardown are never signaled or properly freed, causing a memory leak and permanently hanging userspace waiters.
- [High] Scheduler timeout handler (work_tdr) is not canceled when schedulers are stopped during teardown, allowing a concurrent TDR to resuscitate the device mid-teardown.
Pre-existing issues:
- [High] Concurrent executions of panfrost_reset() from multiple scheduler timeouts corrupt the scheduler pending list.
- [High] Sleeping functions called from DMA fence signalling path during panfrost_reset.
--
commit bbe9e4a95112d42e26f093db1d29071e85c60996
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
Subject: drm/panfrost: Stop all jobs before commencing device teardown
This patch ensures that no jobs are inflight at the time the driver finishes
its structures and synchronizes IRQs. To achieve this, it halts submissions
from existing scheduler entities and forces the hardware to soft-stop all
running jobs during device teardown.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 9549ddb7854a3..9e02fb5f73c82 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -341,6 +341,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
> pm_runtime_get_sync(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
>
> + panfrost_jm_stop_sched_jobs(pfdev);
> +
[Severity: High]
Does this code leak the job structures and GEM references while permanently
hanging userspace waiters?
This adds a call to soft-stop inflight jobs during device teardown without
providing a mechanism to signal their fences. The interrupt handler skips
signaling the fence for soft-stopped jobs (signal_fence = false), expecting
a reset. Stuck jobs are erased from driver state via memset, but panfrost
does not implement the cancel_job callback in drm_sched_backend_ops.
Since they are abandoned on the scheduler's pending_list without being freed
or having their fences signaled, any teardown of the device while jobs are
in flight will trigger this leak and hang.
> panfrost_gem_fini(pfdev);
> panfrost_perfcnt_fini(pfdev);
[ ... ]
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index 5016d2d53da26..630298b7ea8a5 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
[ ... ]
> +void panfrost_jm_stop_sched_jobs(struct panfrost_device *pfdev)
> +{
> + for (u32 i = 0; i < NUM_JOB_SLOTS; i++)
> + drm_sched_wqueue_stop(&pfdev->js->queue[i].sched);
> +
[Severity: High]
Does this create a race window where the timeout handler can execute
concurrently mid-teardown?
The stop logic uses drm_sched_wqueue_stop() which pauses submit queues, but
fails to cancel the delayed timeout work (work_tdr). If the timeout expires
before teardown reaches panfrost_jm_fini(), panfrost_job_timedout() executes
concurrently and triggers a GPU reset. The reset sequence unconditionally
invokes drm_sched_start() and re-enables hardware interrupts, forcing the
GPU and schedulers to resume execution while panfrost_device_fini() destroys
underlying memory and structures.
> + panfrost_stop_jobs(pfdev);
> +}
> +
[Severity: Critical]
Does panfrost_stop_jobs() unintentionally command the hardware to restart
pending jobs during device teardown?
This new teardown function reuses the reset path's soft-stop mechanism
without setting reset.pending. During teardown, the resulting IRQ is handled
by panfrost_jm_handle_irq(). Because this is a teardown and not a GPU reset,
pfdev->reset.pending is 0.
The handler logic unconditionally sees !atomic_read(&pfdev->reset.pending)
and writes JS_COMMAND_START to launch the next queued job. The GPU hardware
resumes executing jobs while panfrost_device_fini() concurrently frees all
backing structures, leading to GPU execution with freed memory and unmapped
page tables.
> +static void
> +panfrost_reset(struct panfrost_device *pfdev,
> + struct drm_sched_job *bad)
> +{
> + unsigned int i;
> + bool cookie;
> +
> + if (!atomic_read(&pfdev->reset.pending))
> + return;
> +
> + /* Stop the schedulers.
> + *
> + * FIXME: We temporarily get out of the dma_fence_signalling section
> + * because the cleanup path generate lockdep splats when taking locks
> + * to release job resources. We should rework the code to follow this
> + * pattern:
> + *
> + * try_lock
> + * if (locked)
> + * release
> + * else
> + * schedule_work_to_release_later
> + */
> + for (i = 0; i < NUM_JOB_SLOTS; i++)
> + drm_sched_stop(&pfdev->js->queue[i].sched, bad);
> +
[Severity: High]
This is a pre-existing issue, but can concurrent executions of
panfrost_reset() from multiple scheduler timeouts corrupt the scheduler
pending list?
Panfrost uses multiple independent DRM schedulers (one per job slot). If
multiple slots timeout simultaneously (e.g. due to a global GPU hang),
multiple work_tdr works fire concurrently. They enter panfrost_job_timedout()
which calls atomic_set(&pfdev->reset.pending, 1) rather than an atomic
exchange.
Both threads bypass the !atomic_read() exclusion check here and concurrently
invoke drm_sched_stop(). Since drm_sched_stop() manipulates
sched->pending_list without internal locks for list_add, the concurrent
modifications corrupt the list.
> + cookie = dma_fence_begin_signalling();
> +
> + if (bad)
> + drm_sched_increase_karma(bad);
> +
> + panfrost_stop_jobs(pfdev);
[Severity: High]
This isn't a bug introduced by this patch (the code was simply refactored
into a helper), but does this invoke sleeping functions from the DMA fence
signalling path?
dma_fence_begin_signalling() enters a critical section where sleeping is
strictly forbidden. Immediately afterward, panfrost_stop_jobs() is called,
which invokes synchronize_irq() (which can block waiting for IRQ handlers)
and readl_poll_timeout() (which sleeps via usleep_range()).
>
> /* Proceed with reset now. */
> panfrost_device_reset(pfdev, false);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=7
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown
2026-08-28 20:56 ` [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown Adrián Larumbe
2026-08-28 21:16 ` sashiko-bot
@ 2026-09-01 12:58 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 12:58 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:47 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> We should make sure there are no inflight jobs at the time we finish driver
> structures and synchronise their IRQs, and even more so by the time clocks
> and power domains are disabled.
>
> To that end, stop submissions from existing scheduler entities and force HW
> to stop all running jobs.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 2 +
> drivers/gpu/drm/panfrost/panfrost_job.c | 71 ++++++++++++++++++------------
> drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
> 3 files changed, 46 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 9549ddb7854a..9e02fb5f73c8 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -341,6 +341,8 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
> pm_runtime_get_sync(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
>
> + panfrost_jm_stop_sched_jobs(pfdev);
Do we really want to do the SOFT_STOP+HARD_STOP dance in that case?
Can't we just SOFT_RESET the GPU and collect the pieces in the
panfrost_<component>_fini() functions?
> +
> panfrost_gem_fini(pfdev);
> panfrost_perfcnt_fini(pfdev);
> panfrost_jm_fini(pfdev);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index 5016d2d53da2..630298b7ea8a 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
> @@ -655,39 +655,12 @@ static u32 panfrost_active_slots(struct panfrost_device *pfdev,
> return js_state & *js_state_mask;
> }
>
> -static void
> -panfrost_reset(struct panfrost_device *pfdev,
> - struct drm_sched_job *bad)
> +static void panfrost_stop_jobs(struct panfrost_device *pfdev)
> {
> u32 js_state, js_state_mask = 0xffffffff;
> unsigned int i, j;
> - bool cookie;
> int ret;
>
> - if (!atomic_read(&pfdev->reset.pending))
> - return;
> -
> - /* Stop the schedulers.
> - *
> - * FIXME: We temporarily get out of the dma_fence_signalling section
> - * because the cleanup path generate lockdep splats when taking locks
> - * to release job resources. We should rework the code to follow this
> - * pattern:
> - *
> - * try_lock
> - * if (locked)
> - * release
> - * else
> - * schedule_work_to_release_later
> - */
> - for (i = 0; i < NUM_JOB_SLOTS; i++)
> - drm_sched_stop(&pfdev->js->queue[i].sched, bad);
> -
> - cookie = dma_fence_begin_signalling();
> -
> - if (bad)
> - drm_sched_increase_karma(bad);
> -
> /* Mask job interrupts and synchronize to make sure we won't be
> * interrupted during our reset.
> */
> @@ -730,6 +703,48 @@ panfrost_reset(struct panfrost_device *pfdev,
> }
> memset(pfdev->jobs, 0, sizeof(pfdev->jobs));
> spin_unlock(&pfdev->js->job_lock);
> +}
> +
> +void panfrost_jm_stop_sched_jobs(struct panfrost_device *pfdev)
> +{
> + for (u32 i = 0; i < NUM_JOB_SLOTS; i++)
> + drm_sched_wqueue_stop(&pfdev->js->queue[i].sched);
Do we have something cleaning up the pending list if this is stopped
while jobs are in-flight? If not, we probably need a ::cancel_job()
implementation.
> +
> + panfrost_stop_jobs(pfdev);
> +}
> +
> +static void
> +panfrost_reset(struct panfrost_device *pfdev,
> + struct drm_sched_job *bad)
> +{
> + unsigned int i;
> + bool cookie;
> +
> + if (!atomic_read(&pfdev->reset.pending))
> + return;
> +
> + /* Stop the schedulers.
> + *
> + * FIXME: We temporarily get out of the dma_fence_signalling section
> + * because the cleanup path generate lockdep splats when taking locks
> + * to release job resources. We should rework the code to follow this
> + * pattern:
> + *
> + * try_lock
> + * if (locked)
> + * release
> + * else
> + * schedule_work_to_release_later
> + */
> + for (i = 0; i < NUM_JOB_SLOTS; i++)
> + drm_sched_stop(&pfdev->js->queue[i].sched, bad);
> +
> + cookie = dma_fence_begin_signalling();
> +
> + if (bad)
> + drm_sched_increase_karma(bad);
> +
> + panfrost_stop_jobs(pfdev);
>
> /* Proceed with reset now. */
> panfrost_device_reset(pfdev, false);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
> index 3e3b9717838f..dc9d3c9dc67b 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.h
> @@ -73,6 +73,7 @@ int panfrost_jm_is_idle(struct panfrost_device *pfdev);
> 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);
> +void panfrost_jm_stop_sched_jobs(struct panfrost_device *pfdev);
>
> #ifdef CONFIG_DEBUG_FS
> void panfrost_sched_debugfs_init(struct drm_minor *minor);
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (6 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 07/17] drm/panfrost: Stop all jobs before commencing device teardown Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:11 ` sashiko-bot
2026-09-01 13:08 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
` (8 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Because MMU interrupts are only enabled when the device is reset, it
happened that after DRM device registration, the very first job targeting
the tiler heap BO would always time out. The reason is the reset sequence
is only part of PM runtime resume, which is not called explicitly at driver
probe time, and an actual reset work item manually triggered after a HW
error.
I have attempted a somewhat drastic solution, which is completely
decoupling GPU/MMU/JM subsystem initialisation and reset from interrupt
enablement, so that we can handle IRQ toggling a bit more flexibly.
To this end:
- Ensure every subsystem with its own IRQ has an 'enable interrupts'
method, and that it doesn't enable them anywhere else.
- Force IRQ masking at MMU reset time. Up until, now, panfrost_mmu_reset()
was clearing the MMU IRQ suspension bit, but at no point that is set during
the reset sequence.
Then manually enable all interrupts when the device is fully initialised at
probe time, right before DRM device registration, or after the reset
sequence is complete. Also disable all interrupts at device remove time,
so that their IRQs can be sync'ed right before tearing the device down.
Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 40 ++++++++++++++++++++++--------
drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++-
drivers/gpu/drm/panfrost/panfrost_gpu.c | 19 ++++++++------
drivers/gpu/drm/panfrost/panfrost_gpu.h | 2 ++
drivers/gpu/drm/panfrost/panfrost_job.c | 7 +++---
drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 +++++--
drivers/gpu/drm/panfrost/panfrost_mmu.h | 2 ++
7 files changed, 56 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 9e02fb5f73c8..99f7da2180f9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -226,6 +226,27 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
return err;
}
+void panfrost_device_enable_int(struct panfrost_device *pfdev)
+{
+ panfrost_gpu_enable_interrupts(pfdev);
+ panfrost_mmu_enable_interrupts(pfdev);
+ panfrost_jm_enable_interrupts(pfdev);
+}
+
+static void panfrost_device_enable_hw(struct panfrost_device *pfdev)
+{
+ panfrost_device_enable_int(pfdev);
+ panfrost_devfreq_resume(pfdev);
+}
+
+static void panfrost_device_disable_hw(struct panfrost_device *pfdev)
+{
+ panfrost_devfreq_suspend(pfdev);
+ panfrost_jm_suspend_irq(pfdev);
+ panfrost_mmu_suspend_irq(pfdev);
+ panfrost_gpu_suspend_irq(pfdev);
+}
+
int panfrost_device_init(struct panfrost_device *pfdev)
{
int err;
@@ -297,6 +318,8 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_perfcnt;
+ panfrost_device_enable_hw(pfdev);
+
pm_runtime_set_active(pfdev->base.dev);
pm_runtime_mark_last_busy(pfdev->base.dev);
pm_runtime_enable(pfdev->base.dev);
@@ -315,6 +338,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
out_devreg:
pm_runtime_disable(pfdev->base.dev);
+ panfrost_device_disable_hw(pfdev);
panfrost_gem_fini(pfdev);
out_perfcnt:
panfrost_perfcnt_fini(pfdev);
@@ -342,6 +366,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
pm_runtime_disable(pfdev->base.dev);
panfrost_jm_stop_sched_jobs(pfdev);
+ panfrost_device_disable_hw(pfdev);
panfrost_gem_fini(pfdev);
panfrost_perfcnt_fini(pfdev);
@@ -456,16 +481,12 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
return false;
}
-void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
+void panfrost_device_reset(struct panfrost_device *pfdev)
{
panfrost_gpu_soft_reset(pfdev);
-
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
-
panfrost_jm_reset_interrupts(pfdev);
- if (enable_job_int)
- panfrost_jm_enable_interrupts(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
@@ -479,8 +500,8 @@ static int panfrost_device_runtime_resume(struct device *dev)
return ret;
}
- panfrost_device_reset(pfdev, true);
- panfrost_devfreq_resume(pfdev);
+ panfrost_device_reset(pfdev);
+ panfrost_device_enable_hw(pfdev);
return 0;
}
@@ -492,10 +513,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
if (!panfrost_jm_is_idle(pfdev))
return -EBUSY;
- panfrost_devfreq_suspend(pfdev);
- panfrost_jm_suspend_irq(pfdev);
- panfrost_mmu_suspend_irq(pfdev);
- panfrost_gpu_suspend_irq(pfdev);
+ panfrost_device_disable_hw(pfdev);
panfrost_gpu_power_off(pfdev);
if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index a0b9a2145fc9..c94546b49662 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -250,7 +250,8 @@ int panfrost_unstable_ioctl_check(void);
int panfrost_device_init(struct panfrost_device *pfdev);
void panfrost_device_fini(struct panfrost_device *pfdev);
-void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int);
+void panfrost_device_enable_int(struct panfrost_device *pfdev);
+void panfrost_device_reset(struct panfrost_device *pfdev);
extern const struct dev_pm_ops panfrost_pm_ops;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 8a15ccce08e9..c8e0b1acc669 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -67,8 +67,6 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
gpu_write(pfdev, GPU_INT_MASK, 0);
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED);
- clear_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
-
gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET);
ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT,
val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000);
@@ -87,12 +85,6 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_MASK_ALL);
- /* Only enable the interrupts we care about */
- gpu_write(pfdev, GPU_INT_MASK,
- GPU_IRQ_MASK_ERROR |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED |
- GPU_IRQ_CLEAN_CACHES_COMPLETED);
-
/*
* All in-flight jobs should have released their cycle
* counter references upon reset, but let us make sure
@@ -504,6 +496,17 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
dev_err(pfdev->base.dev, "l2 power transition timeout");
}
+void panfrost_gpu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ clear_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
+
+ /* Only enable the interrupts we care about */
+ gpu_write(pfdev, GPU_INT_MASK,
+ GPU_IRQ_MASK_ERROR |
+ GPU_IRQ_PERFCNT_SAMPLE_COMPLETED |
+ GPU_IRQ_CLEAN_CACHES_COMPLETED);
+}
+
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
{
set_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
index b4fef11211d5..743d45b00d9f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
@@ -15,6 +15,8 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev);
int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
void panfrost_gpu_power_on(struct panfrost_device *pfdev);
void panfrost_gpu_power_off(struct panfrost_device *pfdev);
+
+void panfrost_gpu_enable_interrupts(struct panfrost_device *pfdev);
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);
void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 630298b7ea8a..a3ff7d644276 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -747,7 +747,7 @@ panfrost_reset(struct panfrost_device *pfdev,
panfrost_stop_jobs(pfdev);
/* Proceed with reset now. */
- panfrost_device_reset(pfdev, false);
+ panfrost_device_reset(pfdev);
/* GPU has been reset, we can clear the reset pending bit. */
atomic_set(&pfdev->reset.pending, 0);
@@ -768,8 +768,8 @@ panfrost_reset(struct panfrost_device *pfdev,
for (i = 0; i < NUM_JOB_SLOTS; i++)
drm_sched_start(&pfdev->js->queue[i].sched, 0);
- /* Re-enable job interrupts now that everything has been restarted. */
- panfrost_jm_enable_interrupts(pfdev);
+ /* Re-enable interrupts now that everything has been restarted. */
+ panfrost_device_enable_int(pfdev);
dma_fence_end_signalling(cookie);
}
@@ -923,7 +923,6 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
}
panfrost_jm_reset_interrupts(pfdev);
- panfrost_jm_enable_interrupts(pfdev);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 5c393ed6e310..7fd89ee4ef9e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -340,7 +340,7 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
{
struct panfrost_mmu *mmu, *mmu_tmp;
- clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+ mmu_write(pfdev, MMU_INT_MASK, 0);
spin_lock(&pfdev->as_lock);
@@ -356,7 +356,6 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
spin_unlock(&pfdev->as_lock);
mmu_write(pfdev, MMU_INT_CLEAR, ~0);
- mmu_write(pfdev, MMU_INT_MASK, ~0);
}
static size_t get_pgsize(u64 addr, size_t size, size_t *count)
@@ -981,6 +980,12 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev)
mmu_write(pfdev, MMU_INT_MASK, 0);
}
+void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev)
+{
+ clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+ mmu_write(pfdev, MMU_INT_MASK, ~0);
+}
+
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev)
{
set_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
index 27c3c65ed074..689cf95caa21 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
@@ -15,6 +15,8 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping);
int panfrost_mmu_init(struct panfrost_device *pfdev);
void panfrost_mmu_fini(struct panfrost_device *pfdev);
void panfrost_mmu_reset(struct panfrost_device *pfdev);
+
+void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev);
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);
int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-08-28 20:56 ` [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
@ 2026-08-28 21:11 ` sashiko-bot
2026-09-01 13:08 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:11 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing IRQ suspension in device probe error paths causes potential bus faults on shared IRQ lines.
--
commit 57c2c2f2f808cb53bd917f5b98f5700e0bc31b52
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Split subsystem init/reset from interrupt enablement
This commit decouples GPU, MMU, and Job Manager initialization and reset
sequences from interrupt enablement. It ensures subsystems only enable their
interrupts explicitly and handles IRQ toggling manually during probe, reset,
and removal.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 9e02fb5f73c82..99f7da2180f92 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -315,6 +338,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> out_devreg:
> pm_runtime_disable(pfdev->base.dev);
> + panfrost_device_disable_hw(pfdev);
> panfrost_gem_fini(pfdev);
> out_perfcnt:
> panfrost_perfcnt_fini(pfdev);
> out_job:
> panfrost_jm_fini(pfdev);
> out_mmu:
> panfrost_mmu_fini(pfdev);
> out_gpu:
> panfrost_gpu_fini(pfdev);
[Severity: High]
This isn't a bug introduced by this patch, but is there a missing IRQ
suspension in the earlier error paths?
If probe fails early (for example, during panfrost_mmu_init() or later), the
error path jumps to out_mmu or out_gpu, calling panfrost_gpu_fini(),
panfrost_clk_fini(), etc. This powers off sub-blocks and disables clocks.
However, because panfrost_gpu_suspend_irq() (and its equivalents) are bypassed
on these early error paths, the is_suspended state remains 0. The IRQ was
already requested with devm_request_irq() in panfrost_gpu_init(), and is only
freed later during devres cleanup.
If the IRQ line is shared with another active device (IRQF_SHARED is used by
the driver), an interrupt in this window could cause panfrost_gpu_irq_handler()
to read GPU_FAULT_STATUS from disabled hardware:
drivers/gpu/drm/panfrost/panfrost_gpu.c:panfrost_gpu_irq_handler() {
if (test_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended))
return IRQ_NONE;
fault_status = gpu_read(pfdev, GPU_FAULT_STATUS);
...
}
Can this lead to a bus fault and kernel panic on systems where the GPU IRQ is
shared?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=8
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-08-28 20:56 ` [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
2026-08-28 21:11 ` sashiko-bot
@ 2026-09-01 13:08 ` Boris Brezillon
2026-09-02 15:41 ` Adrián Larumbe
1 sibling, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:08 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:48 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Because MMU interrupts are only enabled when the device is reset, it
> happened that after DRM device registration, the very first job targeting
> the tiler heap BO would always time out. The reason is the reset sequence
> is only part of PM runtime resume, which is not called explicitly at driver
> probe time, and an actual reset work item manually triggered after a HW
> error.
>
> I have attempted a somewhat drastic solution, which is completely
> decoupling GPU/MMU/JM subsystem initialisation and reset from interrupt
> enablement, so that we can handle IRQ toggling a bit more flexibly.
>
> To this end:
> - Ensure every subsystem with its own IRQ has an 'enable interrupts'
> method, and that it doesn't enable them anywhere else.
> - Force IRQ masking at MMU reset time. Up until, now, panfrost_mmu_reset()
> was clearing the MMU IRQ suspension bit, but at no point that is set during
> the reset sequence.
>
> Then manually enable all interrupts when the device is fully initialised at
> probe time, right before DRM device registration, or after the reset
> sequence is complete. Also disable all interrupts at device remove time,
> so that their IRQs can be sync'ed right before tearing the device down.
>
> Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 40 ++++++++++++++++++++++--------
> drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++-
> drivers/gpu/drm/panfrost/panfrost_gpu.c | 19 ++++++++------
> drivers/gpu/drm/panfrost/panfrost_gpu.h | 2 ++
> drivers/gpu/drm/panfrost/panfrost_job.c | 7 +++---
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 +++++--
> drivers/gpu/drm/panfrost/panfrost_mmu.h | 2 ++
> 7 files changed, 56 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 9e02fb5f73c8..99f7da2180f9 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -226,6 +226,27 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
> return err;
> }
>
> +void panfrost_device_enable_int(struct panfrost_device *pfdev)
> +{
> + panfrost_gpu_enable_interrupts(pfdev);
> + panfrost_mmu_enable_interrupts(pfdev);
> + panfrost_jm_enable_interrupts(pfdev);
> +}
> +
> +static void panfrost_device_enable_hw(struct panfrost_device *pfdev)
> +{
> + panfrost_device_enable_int(pfdev);
> + panfrost_devfreq_resume(pfdev);
> +}
> +
> +static void panfrost_device_disable_hw(struct panfrost_device *pfdev)
> +{
> + panfrost_devfreq_suspend(pfdev);
> + panfrost_jm_suspend_irq(pfdev);
> + panfrost_mmu_suspend_irq(pfdev);
> + panfrost_gpu_suspend_irq(pfdev);
Hm, I think I'd prefer if those suspend/resume_irq() were hidden in
some subcomponent panfrost_<subcomp>_suspend,resume() helpers. And
then we just have to resume/suspend component in the right order
instead of treating IRQs as a standalone object (enabling/disabling
only makes sense if the subcomponent handling those interrupts is
resumed/suspended).
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-09-01 13:08 ` Boris Brezillon
@ 2026-09-02 15:41 ` Adrián Larumbe
2026-09-02 16:05 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:41 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 15:08, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:48 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > Because MMU interrupts are only enabled when the device is reset, it
> > happened that after DRM device registration, the very first job targeting
> > the tiler heap BO would always time out. The reason is the reset sequence
> > is only part of PM runtime resume, which is not called explicitly at driver
> > probe time, and an actual reset work item manually triggered after a HW
> > error.
> >
> > I have attempted a somewhat drastic solution, which is completely
> > decoupling GPU/MMU/JM subsystem initialisation and reset from interrupt
> > enablement, so that we can handle IRQ toggling a bit more flexibly.
> >
> > To this end:
> > - Ensure every subsystem with its own IRQ has an 'enable interrupts'
> > method, and that it doesn't enable them anywhere else.
> > - Force IRQ masking at MMU reset time. Up until, now, panfrost_mmu_reset()
> > was clearing the MMU IRQ suspension bit, but at no point that is set during
> > the reset sequence.
> >
> > Then manually enable all interrupts when the device is fully initialised at
> > probe time, right before DRM device registration, or after the reset
> > sequence is complete. Also disable all interrupts at device remove time,
> > so that their IRQs can be sync'ed right before tearing the device down.
> >
> > Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> > Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 40 ++++++++++++++++++++++--------
> > drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++-
> > drivers/gpu/drm/panfrost/panfrost_gpu.c | 19 ++++++++------
> > drivers/gpu/drm/panfrost/panfrost_gpu.h | 2 ++
> > drivers/gpu/drm/panfrost/panfrost_job.c | 7 +++---
> > drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 +++++--
> > drivers/gpu/drm/panfrost/panfrost_mmu.h | 2 ++
> > 7 files changed, 56 insertions(+), 26 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index 9e02fb5f73c8..99f7da2180f9 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > @@ -226,6 +226,27 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
> > return err;
> > }
> >
> > +void panfrost_device_enable_int(struct panfrost_device *pfdev)
> > +{
> > + panfrost_gpu_enable_interrupts(pfdev);
> > + panfrost_mmu_enable_interrupts(pfdev);
> > + panfrost_jm_enable_interrupts(pfdev);
> > +}
> > +
> > +static void panfrost_device_enable_hw(struct panfrost_device *pfdev)
> > +{
> > + panfrost_device_enable_int(pfdev);
> > + panfrost_devfreq_resume(pfdev);
> > +}
> > +
> > +static void panfrost_device_disable_hw(struct panfrost_device *pfdev)
> > +{
> > + panfrost_devfreq_suspend(pfdev);
> > + panfrost_jm_suspend_irq(pfdev);
> > + panfrost_mmu_suspend_irq(pfdev);
> > + panfrost_gpu_suspend_irq(pfdev);
>
> Hm, I think I'd prefer if those suspend/resume_irq() were hidden in
> some subcomponent panfrost_<subcomp>_suspend,resume() helpers. And
> then we just have to resume/suspend component in the right order
> instead of treating IRQs as a standalone object (enabling/disabling
> only makes sense if the subcomponent handling those interrupts is
> resumed/suspended).
I thought it would only make sense to enable interupts for a given subsystem
when all the other subsystems are also resumed or initialised. This was prompted
by Sashiko warning of the possibility of spurious interrupts causing a handler
to be run when one of the subsystems it touches on hasn't yet been initialised.
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement
2026-09-02 15:41 ` Adrián Larumbe
@ 2026-09-02 16:05 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:05 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:41:40 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 15:08, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:48 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > Because MMU interrupts are only enabled when the device is reset, it
> > > happened that after DRM device registration, the very first job targeting
> > > the tiler heap BO would always time out. The reason is the reset sequence
> > > is only part of PM runtime resume, which is not called explicitly at driver
> > > probe time, and an actual reset work item manually triggered after a HW
> > > error.
> > >
> > > I have attempted a somewhat drastic solution, which is completely
> > > decoupling GPU/MMU/JM subsystem initialisation and reset from interrupt
> > > enablement, so that we can handle IRQ toggling a bit more flexibly.
> > >
> > > To this end:
> > > - Ensure every subsystem with its own IRQ has an 'enable interrupts'
> > > method, and that it doesn't enable them anywhere else.
> > > - Force IRQ masking at MMU reset time. Up until, now, panfrost_mmu_reset()
> > > was clearing the MMU IRQ suspension bit, but at no point that is set during
> > > the reset sequence.
> > >
> > > Then manually enable all interrupts when the device is fully initialised at
> > > probe time, right before DRM device registration, or after the reset
> > > sequence is complete. Also disable all interrupts at device remove time,
> > > so that their IRQs can be sync'ed right before tearing the device down.
> > >
> > > Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> > > Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
> > > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > > ---
> > > drivers/gpu/drm/panfrost/panfrost_device.c | 40 ++++++++++++++++++++++--------
> > > drivers/gpu/drm/panfrost/panfrost_device.h | 3 ++-
> > > drivers/gpu/drm/panfrost/panfrost_gpu.c | 19 ++++++++------
> > > drivers/gpu/drm/panfrost/panfrost_gpu.h | 2 ++
> > > drivers/gpu/drm/panfrost/panfrost_job.c | 7 +++---
> > > drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 +++++--
> > > drivers/gpu/drm/panfrost/panfrost_mmu.h | 2 ++
> > > 7 files changed, 56 insertions(+), 26 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > > index 9e02fb5f73c8..99f7da2180f9 100644
> > > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > > @@ -226,6 +226,27 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
> > > return err;
> > > }
> > >
> > > +void panfrost_device_enable_int(struct panfrost_device *pfdev)
> > > +{
> > > + panfrost_gpu_enable_interrupts(pfdev);
> > > + panfrost_mmu_enable_interrupts(pfdev);
> > > + panfrost_jm_enable_interrupts(pfdev);
> > > +}
> > > +
> > > +static void panfrost_device_enable_hw(struct panfrost_device *pfdev)
> > > +{
> > > + panfrost_device_enable_int(pfdev);
> > > + panfrost_devfreq_resume(pfdev);
> > > +}
> > > +
> > > +static void panfrost_device_disable_hw(struct panfrost_device *pfdev)
> > > +{
> > > + panfrost_devfreq_suspend(pfdev);
> > > + panfrost_jm_suspend_irq(pfdev);
> > > + panfrost_mmu_suspend_irq(pfdev);
> > > + panfrost_gpu_suspend_irq(pfdev);
> >
> > Hm, I think I'd prefer if those suspend/resume_irq() were hidden in
> > some subcomponent panfrost_<subcomp>_suspend,resume() helpers. And
> > then we just have to resume/suspend component in the right order
> > instead of treating IRQs as a standalone object (enabling/disabling
> > only makes sense if the subcomponent handling those interrupts is
> > resumed/suspended).
>
> I thought it would only make sense to enable interupts for a given subsystem
> when all the other subsystems are also resumed or initialised. This was prompted
> by Sashiko warning of the possibility of spurious interrupts causing a handler
> to be run when one of the subsystems it touches on hasn't yet been initialised.
Well, in practice things tend to be well isolated, for instance, an
MMU IRQ should be processed entirely inside panfrost_mmu.c, with no
particular interaction with the other subsystems. So, if an MMU
interrupt fires before, say, the JM subsystem is up and running, that
shouldn't be a problem. In panthor, we have a few cases where events
get propagated between subsystems, and for those we have some
is_initialized checks. I'm not sure this applies to panfrost though.
The other advantage with this approach is that it's one step towards a
better subsystem isolation like we have in panthor, where subsystems
only see their internal state/data plus the general state exposed by
panthor_device, instead of having everything in panfrost_device, and
everyone having the ability to modify/check the state of other
subsystems. panfrost_device.c then just acts as a glue layer that knows
about the order things should be executed in, but doesn't have all the
internal details about subsystem initialization/teardown steps.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (7 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 08/17] drm/panfrost: Split subsystem init/reset from interrupt enablement Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:09 ` sashiko-bot
2026-09-01 13:18 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
` (7 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
During device probe(), failure to do a PM get() will leave the usage_count
set to 0, which is the value assigned at device creation time. That means
when the autosuspend delay expires, runtime suspend callback won't be
invoked, so the device will remain powered on forever.
On top of that, failure to call PM put() during device unplug means
Panfrost device's PM usage_count increases monotonically for every new
module reload.
The combined outcome of both of the above was that devfreq OPP transition
notifications would be printed all the time, even when no jobs are being
submitted. This quickly fills the kernel ring buffer with junk.
To fix the above:
- Manually adjust the PM refcnt at device probe and removal time.
- Ensure pm_runtime_dont_use_autosuspend is called in the wind-down path.
- Call pm_runtime_put_autosuspend() when device is ready to accept jobs
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
---
drivers/gpu/drm/panfrost/panfrost_device.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 99f7da2180f9..70c8109ea698 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -322,6 +322,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
pm_runtime_set_active(pfdev->base.dev);
pm_runtime_mark_last_busy(pfdev->base.dev);
+ pm_runtime_get_noresume(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);
@@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err < 0)
goto out_devreg;
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+
return 0;
out_devreg:
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
panfrost_device_disable_hw(pfdev);
panfrost_gem_fini(pfdev);
@@ -363,7 +368,9 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_dont_use_autosuspend(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
panfrost_jm_stop_sched_jobs(pfdev);
panfrost_device_disable_hw(pfdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-08-28 20:56 ` [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-08-28 21:09 ` sashiko-bot
2026-09-01 13:18 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:09 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Dropping the PM usage counter before disabling runtime PM in the init error path creates a window for unintended device suspend, leading to a potential crash.
Pre-existing issues:
- [High] panfrost_device_fini() unconditionally accesses hardware registers even if pm_runtime_get_sync() fails, leading to a potential crash.
--
commit 2b42dddf42e663beb9d859051cf7ae5891d997e4
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
This commit addresses power management reference count and autosuspend
issues during device probe and removal. It manually adjusts the PM refcount
and calls the autosuspend or noidle functions at the appropriate times to
prevent the device from remaining powered on forever or increasing its
usage count monotonically on module reloads.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 99f7da2180f92..70c8109ea6987 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> if (err < 0)
> goto out_devreg;
>
> + pm_runtime_put_autosuspend(pfdev->base.dev);
> +
> return 0;
>
> out_devreg:
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
[Severity: High]
Does dropping the PM usage counter before disabling runtime PM create a race
window here?
If `pm_runtime_put_noidle()` drops the PM usage count to 0 while PM runtime
is still active, a concurrent thread (e.g., userspace modifying sysfs power
controls) could trigger an asynchronous device suspend. This would turn off
clocks and power domains right before the error path proceeds to call
`panfrost_device_disable_hw()`, attempting to write to memory-mapped hardware
registers that are now powered off, which can cause a Synchronous External
Abort.
Should `pm_runtime_disable()` be called before `pm_runtime_put_noidle()` to
guarantee the device remains active during teardown?
> panfrost_device_disable_hw(pfdev);
> panfrost_gem_fini(pfdev);
[ ... ]
> @@ -363,7 +368,9 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> pm_runtime_get_sync(pfdev->base.dev);
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
>
> panfrost_jm_stop_sched_jobs(pfdev);
> panfrost_device_disable_hw(pfdev);
[Severity: High]
This is a pre-existing issue, but panfrost_device_fini() unconditionally
accesses hardware registers even if pm_runtime_get_sync() fails.
If `pm_runtime_get_sync()` returns `< 0` (e.g., due to a hardware fault,
regulator issue, or clock failure), the function continues unconditionally
to `panfrost_device_disable_hw()`. This attempts to write to GPU/MMU
registers without power/clocks, which will result in a system crash
(Synchronous External Abort).
Can we add a check to handle the `pm_runtime_get_sync()` failure and avoid
accessing the powered-down hardware?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=9
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-08-28 20:56 ` [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
2026-08-28 21:09 ` sashiko-bot
@ 2026-09-01 13:18 ` Boris Brezillon
2026-09-02 15:42 ` Adrián Larumbe
1 sibling, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:18 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:49 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> During device probe(), failure to do a PM get() will leave the usage_count
> set to 0, which is the value assigned at device creation time. That means
> when the autosuspend delay expires, runtime suspend callback won't be
> invoked, so the device will remain powered on forever.
>
> On top of that, failure to call PM put() during device unplug means
> Panfrost device's PM usage_count increases monotonically for every new
> module reload.
>
> The combined outcome of both of the above was that devfreq OPP transition
> notifications would be printed all the time, even when no jobs are being
> submitted. This quickly fills the kernel ring buffer with junk.
>
> To fix the above:
> - Manually adjust the PM refcnt at device probe and removal time.
> - Ensure pm_runtime_dont_use_autosuspend is called in the wind-down path.
> - Call pm_runtime_put_autosuspend() when device is ready to accept jobs
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
Because of how things are currently ordered, this won't be backportable
without patch 2, which doesn't have a Fixes tag, so, either we put the
fix first, and then we move code, or we make it a single commit.
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 99f7da2180f9..70c8109ea698 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -322,6 +322,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> pm_runtime_set_active(pfdev->base.dev);
> pm_runtime_mark_last_busy(pfdev->base.dev);
> + pm_runtime_get_noresume(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);
> @@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> if (err < 0)
> goto out_devreg;
>
> + pm_runtime_put_autosuspend(pfdev->base.dev);
> +
> return 0;
>
> out_devreg:
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> panfrost_device_disable_hw(pfdev);
> panfrost_gem_fini(pfdev);
> @@ -363,7 +368,9 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> pm_runtime_get_sync(pfdev->base.dev);
> + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> + pm_runtime_put_noidle(pfdev->base.dev);
>
> panfrost_jm_stop_sched_jobs(pfdev);
> panfrost_device_disable_hw(pfdev);
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-09-01 13:18 ` Boris Brezillon
@ 2026-09-02 15:42 ` Adrián Larumbe
2026-09-02 16:14 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:42 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 15:18, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:49 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > During device probe(), failure to do a PM get() will leave the usage_count
> > set to 0, which is the value assigned at device creation time. That means
> > when the autosuspend delay expires, runtime suspend callback won't be
> > invoked, so the device will remain powered on forever.
> >
> > On top of that, failure to call PM put() during device unplug means
> > Panfrost device's PM usage_count increases monotonically for every new
> > module reload.
> >
> > The combined outcome of both of the above was that devfreq OPP transition
> > notifications would be printed all the time, even when no jobs are being
> > submitted. This quickly fills the kernel ring buffer with junk.
> >
> > To fix the above:
> > - Manually adjust the PM refcnt at device probe and removal time.
> > - Ensure pm_runtime_dont_use_autosuspend is called in the wind-down path.
> > - Call pm_runtime_put_autosuspend() when device is ready to accept jobs
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> > Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
>
> Because of how things are currently ordered, this won't be backportable
> without patch 2, which doesn't have a Fixes tag, so, either we put the
> fix first, and then we move code, or we make it a single commit.
I think I mentioned it in a previous reply. Maybe it's best to fix the outstanding issues first
and then moving code into panfrost_{init|fini}().
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index 99f7da2180f9..70c8109ea698 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > @@ -322,6 +322,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> >
> > pm_runtime_set_active(pfdev->base.dev);
> > pm_runtime_mark_last_busy(pfdev->base.dev);
> > + pm_runtime_get_noresume(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);
> > @@ -334,9 +335,13 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > if (err < 0)
> > goto out_devreg;
> >
> > + pm_runtime_put_autosuspend(pfdev->base.dev);
> > +
> > return 0;
> >
> > out_devreg:
> > + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> > + pm_runtime_put_noidle(pfdev->base.dev);
> > pm_runtime_disable(pfdev->base.dev);
> > panfrost_device_disable_hw(pfdev);
> > panfrost_gem_fini(pfdev);
> > @@ -363,7 +368,9 @@ int panfrost_device_init(struct panfrost_device *pfdev)
> > void panfrost_device_fini(struct panfrost_device *pfdev)
> > {
> > pm_runtime_get_sync(pfdev->base.dev);
> > + pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> > pm_runtime_disable(pfdev->base.dev);
> > + pm_runtime_put_noidle(pfdev->base.dev);
> >
> > panfrost_jm_stop_sched_jobs(pfdev);
> > panfrost_device_disable_hw(pfdev);
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove
2026-09-02 15:42 ` Adrián Larumbe
@ 2026-09-02 16:14 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:14 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:42:12 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 15:18, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:49 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > During device probe(), failure to do a PM get() will leave the usage_count
> > > set to 0, which is the value assigned at device creation time. That means
> > > when the autosuspend delay expires, runtime suspend callback won't be
> > > invoked, so the device will remain powered on forever.
> > >
> > > On top of that, failure to call PM put() during device unplug means
> > > Panfrost device's PM usage_count increases monotonically for every new
> > > module reload.
> > >
> > > The combined outcome of both of the above was that devfreq OPP transition
> > > notifications would be printed all the time, even when no jobs are being
> > > submitted. This quickly fills the kernel ring buffer with junk.
> > >
> > > To fix the above:
> > > - Manually adjust the PM refcnt at device probe and removal time.
> > > - Ensure pm_runtime_dont_use_autosuspend is called in the wind-down path.
> > > - Call pm_runtime_put_autosuspend() when device is ready to accept jobs
> > >
> > > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > > Fixes: 635430797d3f ("drm/panfrost: Rework runtime PM initialization")
> > > Fixes: 876b15d2c88d ("drm/panfrost: Fix module unload")
> >
> > Because of how things are currently ordered, this won't be backportable
> > without patch 2, which doesn't have a Fixes tag, so, either we put the
> > fix first, and then we move code, or we make it a single commit.
>
> I think I mentioned it in a previous reply. Maybe it's best to fix the outstanding issues first
> and then moving code into panfrost_{init|fini}().
I agree it'd be simpler to do that.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (8 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 09/17] drm/panfrost: Fix PM refcnt and autosuspend issues at device probe/remove Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:10 ` sashiko-bot
2026-09-01 13:20 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
` (6 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Rather than just failing silently, let's warn the user of device remove not
being able to take an PM reference or the PM suspend path still reporting
inflight jobs. Neither situation should ever happen.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 70c8109ea698..d8acae9b8cfa 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -9,6 +9,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -367,7 +368,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
void panfrost_device_fini(struct panfrost_device *pfdev)
{
- pm_runtime_get_sync(pfdev->base.dev);
+ drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
pm_runtime_dont_use_autosuspend(pfdev->base.dev);
pm_runtime_disable(pfdev->base.dev);
pm_runtime_put_noidle(pfdev->base.dev);
@@ -517,7 +518,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
{
struct panfrost_device *pfdev = dev_get_drvdata(dev);
- if (!panfrost_jm_is_idle(pfdev))
+ if (drm_WARN_ON(&pfdev->base, !panfrost_jm_is_idle(pfdev)))
return -EBUSY;
panfrost_device_disable_hw(pfdev);
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions
2026-08-28 20:56 ` [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
@ 2026-08-28 21:10 ` sashiko-bot
2026-09-01 13:20 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:10 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Spurious WARN_ON triggered during system suspend when GPU jobs are active.
Pre-existing issues:
- [High] Unconditional hardware access after `pm_runtime_get_sync()` failure causes kernel panic.
--
commit a7ee5ddc6b752f83ff2b60cb2bbc09648f803bce
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Add warning messages to fatal error conditions
This commit adds drm_WARN_ON macros to panfrost_device_fini and
panfrost_device_runtime_suspend to warn users when PM reference acquisition
fails or when inflight jobs are reported during the PM suspend path.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 70c8109ea6987..d8acae9b8cfac 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -367,7 +368,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> - pm_runtime_get_sync(pfdev->base.dev);
> + drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
> pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> pm_runtime_put_noidle(pfdev->base.dev);
[Severity: High]
This isn't a bug introduced by this patch, but while we are adding a warning
here, does it make sense to continue execution if pm_runtime_get_sync fails?
If the device fails to power on due to a regulator or clock failure, the
warning is logged but the function continues and calls:
panfrost_device_fini()
panfrost_jm_stop_sched_jobs()
panfrost_device_disable_hw()
Both of these subsequent functions write to GPU registers. Accessing hardware
registers of an unclocked or unpowered device on ARM SoCs triggers a Data
Abort exception, leading to a hard kernel panic.
Should we abort the teardown or skip the hardware disable steps if the PM
resume fails?
[ ... ]
> @@ -517,7 +518,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
> {
> struct panfrost_device *pfdev = dev_get_drvdata(dev);
>
> - if (!panfrost_jm_is_idle(pfdev))
> + if (drm_WARN_ON(&pfdev->base, !panfrost_jm_is_idle(pfdev)))
> return -EBUSY;
>
> panfrost_device_disable_hw(pfdev);
[Severity: Medium]
Will this trigger a spurious warning stack trace if the system is suspended
while a GPU workload is still executing?
When a user suspends the system with an active GPU workload, the kernel
freezes userspace tasks but jobs already submitted to the hardware can
continue running. During the system suspend flow, pm_runtime_force_suspend()
intentionally bypasses usage counters and forces a call directly to
panfrost_device_runtime_suspend().
Since the job might still be executing on the hardware, panfrost_jm_is_idle()
can legitimately return false here. Returning -EBUSY to abort the suspend is
the expected behavior, but the new warning assumes this state is always a bug
and will print a stack trace.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=10
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions
2026-08-28 20:56 ` [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
2026-08-28 21:10 ` sashiko-bot
@ 2026-09-01 13:20 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:20 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:50 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Rather than just failing silently, let's warn the user of device remove not
> being able to take an PM reference or the PM suspend path still reporting
> inflight jobs. Neither situation should ever happen.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index 70c8109ea698..d8acae9b8cfa 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -9,6 +9,7 @@
> #include <linux/pm_runtime.h>
> #include <linux/regulator/consumer.h>
> #include <drm/drm_drv.h>
> +#include <drm/drm_print.h>
>
> #include "panfrost_device.h"
> #include "panfrost_devfreq.h"
> @@ -367,7 +368,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
>
> void panfrost_device_fini(struct panfrost_device *pfdev)
> {
> - pm_runtime_get_sync(pfdev->base.dev);
> + drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0);
> pm_runtime_dont_use_autosuspend(pfdev->base.dev);
> pm_runtime_disable(pfdev->base.dev);
> pm_runtime_put_noidle(pfdev->base.dev);
> @@ -517,7 +518,7 @@ static int panfrost_device_runtime_suspend(struct device *dev)
> {
> struct panfrost_device *pfdev = dev_get_drvdata(dev);
>
> - if (!panfrost_jm_is_idle(pfdev))
> + if (drm_WARN_ON(&pfdev->base, !panfrost_jm_is_idle(pfdev)))
> return -EBUSY;
>
> panfrost_device_disable_hw(pfdev);
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (9 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 10/17] drm/panfrost: Add warning messages to fatal error conditions Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:12 ` sashiko-bot
2026-09-01 13:27 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 12/17] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
` (5 subsequent siblings)
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
This will be of great help when testing potential races between the GPU
reset sequence and other parts of the code accessing HW registers.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 35 ++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index d8acae9b8cfa..b6a48ae0d3a6 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>
@@ -600,9 +601,43 @@ 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);
+
+ if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
+ panfrost_device_schedule_reset(pfdev);
+ flush_work(&pfdev->reset.work);
+ pm_runtime_put(pfdev->base.dev);
+ }
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops,
+ reset_get, reset_set,
+ "0x%08llx\n");
+
+static void panfrost_reset_debugfs_init(struct drm_minor *minor)
+{
+ debugfs_create_file("reset", 0600, minor->debugfs_root,
+ minor->dev, &panfrost_reset_debugfs_fops);
+}
+
void panfrost_device_debugfs_init(struct drm_minor *minor)
{
panfrost_gems_debugfs_init(minor);
panfrost_sched_debugfs_init(minor);
+ panfrost_reset_debugfs_init(minor);
}
#endif // CONFIG_DEBUG_FS
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-08-28 20:56 ` [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-08-28 21:12 ` sashiko-bot
2026-09-01 13:27 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:12 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Incorrect handling of pm_runtime_get_if_in_use() return value in reset_set() leading to usage count underflow.
Pre-existing issues:
- [High] Incorrect handling of pm_runtime_get_if_in_use() return value in panfrost_gpu_get_latest_flush_id() causing unsafe MMIO reads and PM underflow.
--
commit 6c0954bdc79e7702d546ce41292da37dbd1a90e1
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Add debugfs knob for manually triggering a GPU reset
This patch adds a debugfs file for manually triggering a GPU reset to aid
in testing potential races between the reset sequence and hardware register
access.
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index d8acae9b8cfac..b6a48ae0d3a61 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
[ ... ]
> @@ -600,9 +601,43 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = {
> };
>
> #ifdef CONFIG_DEBUG_FS
[ ... ]
> +static int reset_set(void *data, u64 val)
> +{
> + struct panfrost_device *pfdev =
> + container_of(data, struct panfrost_device, base);
> +
> + if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
[Severity: Medium]
When pm_runtime_get_if_in_use() is called while runtime PM is disabled
(such as during driver unbind or system suspend), it returns -EINVAL.
Since -EINVAL evaluates to true in this condition, does this cause the
block to execute without a successful get?
If so, could this regression lead to an unbalanced pm_runtime_put() call
at the end of the block and a PM usage counter underflow?
> + panfrost_device_schedule_reset(pfdev);
> + flush_work(&pfdev->reset.work);
> + pm_runtime_put(pfdev->base.dev);
> + }
> +
> + return 0;
> +}
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but looking at a similar
pre-existing pattern in drivers/gpu/drm/panfrost/panfrost_gpu.c:
panfrost_gpu_get_latest_flush_id() {
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) {
if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
flush_id = gpu_read(pfdev, GPU_LATEST_FLUSH_ID);
pm_runtime_put(pfdev->base.dev);
return flush_id;
}
}
...
}
If pm_runtime_get_if_in_use() returns -EINVAL during system suspend or
teardown when disable_depth > 0, does this condition also evaluate to true?
Could this result in an unsafe MMIO read on potentially powered-off
hardware, followed by another unbalanced usage count decrement?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=11
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-08-28 20:56 ` [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
2026-08-28 21:12 ` sashiko-bot
@ 2026-09-01 13:27 ` Boris Brezillon
2026-09-02 15:42 ` Adrián Larumbe
1 sibling, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:27 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:51 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> This will be of great help when testing potential races between the GPU
> reset sequence and other parts of the code accessing HW registers.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 35 ++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index d8acae9b8cfa..b6a48ae0d3a6 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>
> @@ -600,9 +601,43 @@ 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);
> +
> + if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
Are you sure it's not pm_runtime_get_if_active() we want here? If use
the _if_in_use() variant and autosuspend is enabled, we might skip a
reset on a device that's active.
> + panfrost_device_schedule_reset(pfdev);
> + flush_work(&pfdev->reset.work);
> + pm_runtime_put(pfdev->base.dev);
> + }
> +
> + return 0;
> +}
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops,
> + reset_get, reset_set,
> + "0x%08llx\n");
> +
> +static void panfrost_reset_debugfs_init(struct drm_minor *minor)
> +{
> + debugfs_create_file("reset", 0600, minor->debugfs_root,
> + minor->dev, &panfrost_reset_debugfs_fops);
> +}
> +
> void panfrost_device_debugfs_init(struct drm_minor *minor)
> {
> panfrost_gems_debugfs_init(minor);
> panfrost_sched_debugfs_init(minor);
> + panfrost_reset_debugfs_init(minor);
> }
> #endif // CONFIG_DEBUG_FS
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-09-01 13:27 ` Boris Brezillon
@ 2026-09-02 15:42 ` Adrián Larumbe
2026-09-02 16:23 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:42 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 15:27, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:51 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > This will be of great help when testing potential races between the GPU
> > reset sequence and other parts of the code accessing HW registers.
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 35 ++++++++++++++++++++++++++++++
> > 1 file changed, 35 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index d8acae9b8cfa..b6a48ae0d3a6 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>
> > @@ -600,9 +601,43 @@ 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);
> > +
> > + if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
>
> Are you sure it's not pm_runtime_get_if_active() we want here? If use
> the _if_in_use() variant and autosuspend is enabled, we might skip a
> reset on a device that's active.
Do you mean if a driver has brought the RPM count down to 0 and scheduled a deferred suspend?
Couldn't manually triggering a reset then somehow race with whatever is being done in
panfrost_device_runtime_suspend() ?
> > + panfrost_device_schedule_reset(pfdev);
> > + flush_work(&pfdev->reset.work);
> > + pm_runtime_put(pfdev->base.dev);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops,
> > + reset_get, reset_set,
> > + "0x%08llx\n");
> > +
> > +static void panfrost_reset_debugfs_init(struct drm_minor *minor)
> > +{
> > + debugfs_create_file("reset", 0600, minor->debugfs_root,
> > + minor->dev, &panfrost_reset_debugfs_fops);
> > +}
> > +
> > void panfrost_device_debugfs_init(struct drm_minor *minor)
> > {
> > panfrost_gems_debugfs_init(minor);
> > panfrost_sched_debugfs_init(minor);
> > + panfrost_reset_debugfs_init(minor);
> > }
> > #endif // CONFIG_DEBUG_FS
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset
2026-09-02 15:42 ` Adrián Larumbe
@ 2026-09-02 16:23 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:23 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:42:53 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 15:27, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:51 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > This will be of great help when testing potential races between the GPU
> > > reset sequence and other parts of the code accessing HW registers.
> > >
> > > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > > ---
> > > drivers/gpu/drm/panfrost/panfrost_device.c | 35 ++++++++++++++++++++++++++++++
> > > 1 file changed, 35 insertions(+)
> > >
> > > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > > index d8acae9b8cfa..b6a48ae0d3a6 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>
> > > @@ -600,9 +601,43 @@ 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);
> > > +
> > > + if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
> >
> > Are you sure it's not pm_runtime_get_if_active() we want here? If use
> > the _if_in_use() variant and autosuspend is enabled, we might skip a
> > reset on a device that's active.
>
> Do you mean if a driver has brought the RPM count down to 0 and scheduled a deferred suspend?
> Couldn't manually triggering a reset then somehow race with whatever is being done in
> panfrost_device_runtime_suspend() ?
If a concurrent suspend is happening, _get_if_active() would wait for
the transition to happen, and return false when the suspend is
effective. If a suspend was scheduled (rpm ref was zero), it will be
cancelled, and you'll end up with an RPM ref preventing any suspend from
happening until you call pm_runtime_put(). So I think we're good.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 12/17] drm/panfrost: Move perfcnt GPU disable sequence into a helper
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (10 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 11/17] drm/panfrost: Add debugfs knob for manually triggering a GPU reset Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
` (4 subsequent siblings)
16 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Just for the sake of avoiding repetition.
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 31 ++++++++++++-----------------
1 file changed, 13 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index 7020c0192e18..f71534e741b6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -35,6 +35,16 @@ struct panfrost_perfcnt {
struct completion dump_comp;
};
+static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
+{
+ gpu_write(pfdev, GPU_PERFCNT_CFG,
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+}
+
void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
{
complete(&pfdev->perfcnt->dump_comp);
@@ -193,12 +203,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
if (user != perfcnt->user)
return -EINVAL;
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
+ panfrost_perfcnt_hw_disable(pfdev);
perfcnt->user = NULL;
drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
@@ -327,12 +332,7 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
perfcnt->bosize = size;
/* Start with everything disabled. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ panfrost_perfcnt_hw_disable(pfdev);
init_completion(&perfcnt->dump_comp);
mutex_init(&perfcnt->lock);
@@ -344,10 +344,5 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
{
/* Disable everything before leaving. */
- gpu_write(pfdev, GPU_PERFCNT_CFG,
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0);
- gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ panfrost_perfcnt_hw_disable(pfdev);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (11 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 12/17] drm/panfrost: Move perfcnt GPU disable sequence into a helper Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-09-01 13:32 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
` (3 subsequent siblings)
16 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
The GPU cache flush/invalidate operation is unnecessary, because at present
all counters are always enabled for all sessions, and there are no plans to
expand the uAPI so as to let UM select specific counter groups.
If the above was ever implemented, then different counter groups being
selected between sessions might leave stale data in the GPU caches, so
the flush/invalidate would have to be reintroduced.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index f71534e741b6..ffc77121070e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -124,21 +124,10 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
/*
- * Invalidate the cache and clear the counters to start from a fresh
- * state.
+ * Clear the counters to start from a fresh state.
*/
- reinit_completion(&pfdev->perfcnt->dump_comp);
- gpu_write(pfdev, GPU_INT_CLEAR,
- GPU_IRQ_CLEAN_CACHES_COMPLETED |
- GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_INV_CACHES);
- ret = wait_for_completion_timeout(&pfdev->perfcnt->dump_comp,
- msecs_to_jiffies(1000));
- if (!ret) {
- ret = -ETIMEDOUT;
- goto err_vunmap;
- }
ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
if (ret < 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-08-28 20:56 ` [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
@ 2026-09-01 13:32 ` Boris Brezillon
2026-09-02 15:43 ` Adrián Larumbe
0 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:32 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:53 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> The GPU cache flush/invalidate operation is unnecessary, because at present
> all counters are always enabled for all sessions, and there are no plans to
> expand the uAPI so as to let UM select specific counter groups.
>
> If the above was ever implemented, then different counter groups being
> selected between sessions might leave stale data in the GPU caches, so
> the flush/invalidate would have to be reintroduced.
This explanation confuses me. To me, the reason we don't need a
cache-flush has nothing to do with the fact we always enable all
counters, it's just that, either the perfcnt buffer has been freshly
allocated/mapped and the cache has no entry pointing to it, or it's
been written already, and the flush should have happened after the
dump. For the case where the enable happens after a RESET (which you
introduce in one of the remaining commits, I guess), there's no flush
needed either, because the RESET clears all the caches.
>
> 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)
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-09-01 13:32 ` Boris Brezillon
@ 2026-09-02 15:43 ` Adrián Larumbe
2026-09-02 16:29 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:43 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 15:32, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:53 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > The GPU cache flush/invalidate operation is unnecessary, because at present
> > all counters are always enabled for all sessions, and there are no plans to
> > expand the uAPI so as to let UM select specific counter groups.
> >
> > If the above was ever implemented, then different counter groups being
> > selected between sessions might leave stale data in the GPU caches, so
> > the flush/invalidate would have to be reintroduced.
>
> This explanation confuses me. To me, the reason we don't need a
> cache-flush has nothing to do with the fact we always enable all
> counters, it's just that, either the perfcnt buffer has been freshly
> allocated/mapped and the cache has no entry pointing to it, or it's
> been written already, and the flush should have happened after the
> dump. For the case where the enable happens after a RESET (which you
> introduce in one of the remaining commits, I guess), there's no flush
> needed either, because the RESET clears all the caches.
This is something I wrote after a chat with Steven about whether removing this flush/invalidate was fine.
If memory serves me right, he expressed his concern that flushing GPU caches after a sample might write
disabled counter data into system memory, but in that case UM should know better, and I guess reading off
disabled counter data is seen as undefined behaviour in the TRM.
> >
> > 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)
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt
2026-09-02 15:43 ` Adrián Larumbe
@ 2026-09-02 16:29 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:29 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:43:34 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 15:32, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:53 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > The GPU cache flush/invalidate operation is unnecessary, because at present
> > > all counters are always enabled for all sessions, and there are no plans to
> > > expand the uAPI so as to let UM select specific counter groups.
> > >
> > > If the above was ever implemented, then different counter groups being
> > > selected between sessions might leave stale data in the GPU caches, so
> > > the flush/invalidate would have to be reintroduced.
> >
> > This explanation confuses me. To me, the reason we don't need a
> > cache-flush has nothing to do with the fact we always enable all
> > counters, it's just that, either the perfcnt buffer has been freshly
> > allocated/mapped and the cache has no entry pointing to it, or it's
> > been written already, and the flush should have happened after the
> > dump. For the case where the enable happens after a RESET (which you
> > introduce in one of the remaining commits, I guess), there's no flush
> > needed either, because the RESET clears all the caches.
>
> This is something I wrote after a chat with Steven about whether removing this flush/invalidate was fine.
> If memory serves me right, he expressed his concern that flushing GPU caches after a sample might write
> disabled counter data into system memory, but in that case UM should know better, and I guess reading off
> disabled counter data is seen as undefined behaviour in the TRM.
That's still 'after a SAMPLE' though. I see no reason why one would
want to flush things at enable time. To me, the ENABLE step is just
about resetting the internal counters and activating the counting.
Those values are not written directly to memory, the SAMPLE operation
is what transfer those internal counters to the piece of memory you've
provided, and that's when you want the FLUSH to happen, I think.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (12 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 13/17] drm/panfrost: Skip cache flush/invalidate when enabling perfcnt Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:14 ` sashiko-bot
` (2 more replies)
2026-08-28 20:56 ` [PATCH v7 15/17] drm/panfrost: Introduce a reset lock Adrián Larumbe
` (2 subsequent siblings)
16 siblings, 3 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
If the GPU and CPU are fully coherent, then there's no need to flush the
GPU caches after a perfcnt sample is made available.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index ffc77121070e..b3f71d7fd82a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
{
- gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ if (pfdev->features.selected_coherency != COHERENCY_ACE)
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
+ else
+ complete(&pfdev->perfcnt->dump_comp);
}
static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-28 20:56 ` [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-08-28 21:14 ` sashiko-bot
2026-09-01 13:37 ` Boris Brezillon
2026-09-02 16:34 ` Boris Brezillon
2 siblings, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:14 UTC (permalink / raw)
To: Adrián Larumbe; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The GPU interrupt handler acknowledges hardware interrupts after signaling the waitqueue completion, creating a race window where a subsequent hardware operation's interrupt is erroneously cleared.
--
commit a4bf01da864f9ea533a5e37d387ba8e2c63a2a99
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
This commit optimizes the performance counter sample path by skipping the GPU
cache flush on fully coherent (ACE) systems. It directly signals completion
instead of 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]
Does bypassing the cache clean step and calling complete() directly here
expose a race condition with the GPU interrupt handler?
In the baseline, the final completion was triggered by a different bit
(GPU_IRQ_CLEAN_CACHES_COMPLETED). With this patch, the
GPU_IRQ_PERFCNT_SAMPLE_COMPLETED handler path now calls complete() directly
on ACE coherent systems.
If the awoken user thread immediately issues a new dump, and the GPU completes
it before the original IRQ handler in panfrost_gpu_irq_handler() acknowledges
the hardware interrupt:
panfrost_gpu.c:panfrost_gpu_irq_handler()
...
// Software completion happens here (which now wakes the user thread)
panfrost_perfcnt_sample_done(pfdev);
...
// Meanwhile, a new dump is requested and finishes, raising a new interrupt
...
// The original handler then clears the interrupt using its stale state
gpu_write(pfdev, GPU_INT_CLEAR, state);
Could the original handler inadvertently clear the new interrupt because its
local state variable still holds the GPU_IRQ_PERFCNT_SAMPLE_COMPLETED bit?
This could cause back-to-back performance counter dumps (e.g., if a local
user calls the perfcnt dump ioctl in a tight loop) to sporadically fail with
an ioctl timeout when their completion interrupt is lost.
> }
>
> static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=14
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-28 20:56 ` [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
2026-08-28 21:14 ` sashiko-bot
@ 2026-09-01 13:37 ` Boris Brezillon
2026-09-02 15:44 ` Adrián Larumbe
2026-09-02 16:34 ` Boris Brezillon
2 siblings, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 13:37 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:54 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> If the GPU and CPU are fully coherent, then there's no need to flush the
> GPU caches after a perfcnt sample is made available.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> index ffc77121070e..b3f71d7fd82a 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> @@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
>
> void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
> {
> - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
> + if (pfdev->features.selected_coherency != COHERENCY_ACE)
I think ACE_LITE would allow us to skip the flush to, so
if (pfdev->features.selected_coherency == COHERENCY_NONE)
> + 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)
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-09-01 13:37 ` Boris Brezillon
@ 2026-09-02 15:44 ` Adrián Larumbe
2026-09-02 16:33 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:44 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 15:37, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:54 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > If the GPU and CPU are fully coherent, then there's no need to flush the
> > GPU caches after a perfcnt sample is made available.
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 5 ++++-
> > 1 file changed, 4 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > index ffc77121070e..b3f71d7fd82a 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > @@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
> >
> > void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
> > {
> > - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
> > + if (pfdev->features.selected_coherency != COHERENCY_ACE)
>
> I think ACE_LITE would allow us to skip the flush to, so
I thought ACE_LITE means the GPU can snoop CPU caches but not the other way round, so at the end of a
sample, we still have to flush GPU caches for the CPU to see counter data.
> if (pfdev->features.selected_coherency == COHERENCY_NONE)
>
> > + 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)
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-09-02 15:44 ` Adrián Larumbe
@ 2026-09-02 16:33 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:33 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:44:24 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 15:37, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:54 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > If the GPU and CPU are fully coherent, then there's no need to flush the
> > > GPU caches after a perfcnt sample is made available.
> > >
> > > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > > ---
> > > drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 5 ++++-
> > > 1 file changed, 4 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > index ffc77121070e..b3f71d7fd82a 100644
> > > --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > @@ -52,7 +52,10 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev)
> > >
> > > void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
> > > {
> > > - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES);
> > > + if (pfdev->features.selected_coherency != COHERENCY_ACE)
> >
> > I think ACE_LITE would allow us to skip the flush to, so
>
> I thought ACE_LITE means the GPU can snoop CPU caches but not the other way round, so at the end of a
> sample, we still have to flush GPU caches for the CPU to see counter data.
You're absolutely right, sorry for the brainfart.
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems
2026-08-28 20:56 ` [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
2026-08-28 21:14 ` sashiko-bot
2026-09-01 13:37 ` Boris Brezillon
@ 2026-09-02 16:34 ` Boris Brezillon
2 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:34 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:54 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> If the GPU and CPU are fully coherent, then there's no need to flush the
> GPU caches after a perfcnt sample is made available.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@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)
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 15/17] drm/panfrost: Introduce a reset lock
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (13 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 14/17] drm/panfrost: Avoid cache flush after perfcnt sample in fully coherent systems Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
2026-08-28 20:56 ` [PATCH v7 17/17] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field Adrián Larumbe
16 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
So as to avoid accessing HW register while a reset is ongoing, a read/write
semaphore that envelopes the reset sequence will help driver entry points
avoid racing with it. For now, the only such racy entry point is the ioctl
that returns the current GPU timestmap.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 2 ++
drivers/gpu/drm/panfrost/panfrost_device.h | 1 +
drivers/gpu/drm/panfrost/panfrost_drv.c | 9 ++++++---
drivers/gpu/drm/panfrost/panfrost_job.c | 1 +
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index b6a48ae0d3a6..c8c5dc26b03d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -492,6 +492,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
void panfrost_device_reset(struct panfrost_device *pfdev)
{
+ guard(rwsem_write)(&pfdev->reset.lock);
+
panfrost_gpu_soft_reset(pfdev);
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index c94546b49662..0570cb1d650d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -165,6 +165,7 @@ struct panfrost_device {
struct {
struct workqueue_struct *wq;
struct work_struct work;
+ struct rw_semaphore lock;
atomic_t pending;
} reset;
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index f77780c72a1a..571a26b84126 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -41,9 +41,12 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
if (ret)
return ret;
- panfrost_cycle_counter_get(pfdev);
- *arg = panfrost_timestamp_read(pfdev);
- panfrost_cycle_counter_put(pfdev);
+ /* We should not read timestamp register while the GPU is being reset */
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_cycle_counter_get(pfdev);
+ *arg = panfrost_timestamp_read(pfdev);
+ panfrost_cycle_counter_put(pfdev);
+ }
pm_runtime_put(pfdev->base.dev);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index a3ff7d644276..e7ffbba58cf9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -891,6 +891,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] 58+ messages in thread* [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (14 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 15/17] drm/panfrost: Introduce a reset lock Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
2026-08-28 21:17 ` sashiko-bot
2026-09-01 14:03 ` Boris Brezillon
2026-08-28 20:56 ` [PATCH v7 17/17] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field Adrián Larumbe
16 siblings, 2 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Formerly, the reset sequence would race with panfrost_mmu_as_put()
when tearing down a perfcnt session. On top of that, poking GPU
registers to program a perfcnt session or obtaining a dump might lead to
undefined behaviour when done at the same time a reset was ongoing.
Use the reset r/w semaphore to govern access to the hardware at reset
time. On top of that, expand the DRM uAPI for the perfcnt DUMP operation
so that userspace can be made aware of a reset having happened, because
that means counters will go back to 0 and can no longer be accumulated
to values previously kept in user space.
The new perfcnt-aware reset sequence also takes care to reestablish
perfcnt to its original configuration if there was an enabled session.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 1 +
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 189 ++++++++++++++++++++--------
drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
include/uapi/drm/panfrost_drm.h | 3 +-
4 files changed, 140 insertions(+), 54 deletions(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index c8c5dc26b03d..471bd4b037e6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -498,6 +498,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev)
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
panfrost_jm_reset_interrupts(pfdev);
+ panfrost_perfcnt_reset(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index b3f71d7fd82a..0564aa2753f9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -11,6 +11,7 @@
#include <drm/drm_file.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_features.h"
@@ -25,14 +26,18 @@
#define BYTES_PER_COUNTER 4
#define BLOCKS_PER_COREGROUP 8
#define V4_SHADERS_PER_COREGROUP 4
+#define PERFCNT_DUMP_MAX_RETRIES 5
struct panfrost_perfcnt {
struct panfrost_gem_mapping *mapping;
+ unsigned int counterset;
size_t bosize;
void *buf;
struct panfrost_file_priv *user;
struct mutex lock;
struct completion dump_comp;
+ bool reset_happened;
+ bool reset_failed;
};
static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
@@ -58,25 +63,107 @@ 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);
+ ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ if (ret < 0)
+ return ret;
+
+ as = ret;
+ cfg = GPU_PERFCNT_CFG_AS(as) |
+ GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
+
+ /*
+ * Bifrost GPUs have 2 set of counters, but we're only interested by
+ * the first one for now.
+ */
+ if (panfrost_model_is_bifrost(pfdev))
+ cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
+
+ gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
+ gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
+
+ /*
+ * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
+ * counters.
+ */
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
+ else
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
+
+ if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
+ gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
+
+ return 0;
+}
+
+static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
+ u64 *reset_happened)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+ u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
+ s64 retries = PERFCNT_DUMP_MAX_RETRIES;
+ bool retry;
+ int ret;
+
+dump_retry:
+ retry = false;
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ *reset_happened = perfcnt->reset_happened;
+ perfcnt->reset_happened = false;
+ if (perfcnt->reset_failed) {
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ return ret;
+ perfcnt->reset_failed = false;
+ }
+
+ reinit_completion(&pfdev->perfcnt->dump_comp);
+
+ gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
+ gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
+ gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
+ GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
+ gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
+ }
+
+ /*
+ * Here we release the reset semaphore because perfcnt should not get in the way
+ * of a HW reset. The downside is in the gap between the completion is flagged
+ * by the GPU IRQ handler and the sempahore taken once again, yet another reset
+ * could happen, which would lead to a valid sample being discarded. This is
+ * a solution of compromise between ensuring synchronisation with the reset
+ * sequence and letting it happen as quickly as possible for jobs to be rerun.
+ */
+
ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
msecs_to_jiffies(1000));
- if (!ret)
- ret = -ETIMEDOUT;
- else if (ret > 0)
- ret = 0;
+
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ if (ret > 0) {
+ if (perfcnt->reset_happened) {
+ if (--retries >= 0)
+ retry = true;
+ else
+ ret = -EBUSY;
+ } else {
+ ret = 0;
+ }
+ } else if (!ret) {
+ ret = -ETIMEDOUT;
+ }
+ }
+
+ if (retry)
+ goto dump_retry;
return ret;
}
@@ -87,9 +174,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
{
struct panfrost_file_priv *user = file_priv->driver_priv;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- struct iosys_map map;
struct drm_gem_shmem_object *bo;
- u32 cfg, as;
+ struct iosys_map map;
int ret;
if (user == perfcnt->user)
@@ -122,7 +208,9 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
ret = drm_gem_vmap(&bo->base, &map);
if (ret)
goto err_put_mapping;
+
perfcnt->buf = map.vaddr;
+ perfcnt->counterset = counterset;
panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
@@ -132,44 +220,19 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
- ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
- if (ret < 0)
- goto err_vunmap;
-
- as = ret;
- cfg = GPU_PERFCNT_CFG_AS(as) |
- GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
-
- /*
- * Bifrost GPUs have 2 set of counters, but we're only interested by
- * the first one for now.
- */
- if (panfrost_model_is_bifrost(pfdev))
- cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
-
- gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
- gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ ret = panfrost_perfcnt_hw_enable(pfdev);
+ if (ret)
+ goto err_vunmap;
- /*
- * 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);
+ perfcnt->reset_happened = false;
+ perfcnt->reset_failed = false;
+ perfcnt->user = user;
+ }
/* The BO ref is retained by the mapping. */
drm_gem_object_put(&bo->base);
- perfcnt->user = user;
-
return 0;
err_vunmap:
@@ -195,13 +258,16 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
if (user != perfcnt->user)
return -EINVAL;
- panfrost_perfcnt_hw_disable(pfdev);
+ scoped_guard(rwsem_read, &pfdev->reset.lock) {
+ panfrost_perfcnt_hw_disable(pfdev);
+ if (!perfcnt->reset_failed)
+ panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
+ perfcnt->user = NULL;
+ }
- perfcnt->user = NULL;
drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
perfcnt->buf = NULL;
panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
- panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
panfrost_gem_mapping_put(perfcnt->mapping);
perfcnt->mapping = NULL;
pm_runtime_put_autosuspend(pfdev->base.dev);
@@ -255,7 +321,7 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
goto out;
}
- ret = panfrost_perfcnt_dump_locked(pfdev);
+ ret = panfrost_perfcnt_dump_locked(pfdev, &req->hw_reset);
if (ret)
goto out;
@@ -338,3 +404,20 @@ void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
/* Disable everything before leaving. */
panfrost_perfcnt_hw_disable(pfdev);
}
+
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev)
+{
+ struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
+
+ if (drm_WARN_ON(&pfdev->base, !perfcnt))
+ return;
+
+ lockdep_assert_held(&pfdev->reset.lock);
+
+ if (!perfcnt->user)
+ return;
+
+ perfcnt->reset_failed = !!panfrost_perfcnt_hw_enable(pfdev);
+ perfcnt->reset_happened = true;
+ complete(&perfcnt->dump_comp);
+}
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
index 8bbcf5f5fb33..8b9bc704b634 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
@@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void panfrost_perfcnt_reset(struct panfrost_device *pfdev);
#endif
diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h
index 50d5337f35ef..3bbf9220103d 100644
--- a/include/uapi/drm/panfrost_drm.h
+++ b/include/uapi/drm/panfrost_drm.h
@@ -47,7 +47,7 @@ extern "C" {
* them for anything but debugging purpose.
*/
#define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable)
-#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
+#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
#define PANFROST_JD_REQ_FS (1 << 0)
#define PANFROST_JD_REQ_CYCLE_COUNT (1 << 1)
@@ -272,6 +272,7 @@ struct drm_panfrost_perfcnt_enable {
struct drm_panfrost_perfcnt_dump {
__u64 buf_ptr;
+ __u64 hw_reset;
};
/* madvise provides a way to tell the kernel in case a buffers contents
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread* Re: [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence
2026-08-28 20:56 ` [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
@ 2026-08-28 21:17 ` sashiko-bot
2026-09-01 14:03 ` Boris Brezillon
1 sibling, 0 replies; 58+ messages in thread
From: sashiko-bot @ 2026-08-28 21:17 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:
New issues:
- [High] Hardware reset notifications are permanently lost on interrupted ioctl waits.
- [High] Unprotected GPU register writes to GPU_CMD race with device reset.
- [Low] Mixing goto-based error handling with scoped_guard() cleanup in the same function.
Pre-existing issues:
- [High] Spurious WARN_ON due to an off-by-one boundary check in panfrost_mmu_as_get.
--
commit f478a9bf05ba523f5eb295d8a3b40bf995a8444e
Author: Adrián Larumbe <adrian.larumbe@collabora.com>
drm/panfrost: Fix races between perfcnt and reset sequence
This commit updates the reset sequence to use the reset r/w semaphore to
govern hardware access. It also expands the DRM uAPI for the perfcnt dump
operation to inform userspace when a reset occurs, which resets the counters
back to zero.
> 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
[ ... ]
> @@ -58,25 +63,107 @@ void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev)
[ ... ]
> +static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
> + u64 *reset_happened)
> +{
[ ... ]
> +dump_retry:
> + retry = false;
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + *reset_happened = perfcnt->reset_happened;
> + perfcnt->reset_happened = false;
[Severity: High]
If this wait is interrupted by a signal,
wait_for_completion_interruptible_timeout() returns -ERESTARTSYS. Because
we already cleared perfcnt->reset_happened to false above, when the syscall
is automatically restarted, will it see the cleared flag and falsely report
hw_reset = 0 to userspace?
Could this cause userspace to permanently lose notifications about hardware
resets?
> + if (perfcnt->reset_failed) {
> + ret = panfrost_perfcnt_hw_enable(pfdev);
> + if (ret)
> + return ret;
> + perfcnt->reset_failed = false;
> + }
> +
> + reinit_completion(&pfdev->perfcnt->dump_comp);
> +
> + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
> + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
> + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
> + GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
> + }
> +
> + /*
> + * Here we release the reset semaphore because perfcnt should not get in the way
> + * of a HW reset. The downside is in the gap between the completion is flagged
> + * by the GPU IRQ handler and the sempahore taken once again, yet another reset
> + * could happen, which would lead to a valid sample being discarded. This is
> + * a solution of compromise between ensuring synchronisation with the reset
> + * sequence and letting it happen as quickly as possible for jobs to be rerun.
> + */
> +
> ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
> msecs_to_jiffies(1000));
[ ... ]
> @@ -132,44 +220,19 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
[ ... ]
> /*
> * 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);
[Severity: High]
Are these register writes exposed to the exact race condition this patch
intends to fix?
Executing gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR) without holding
pfdev->reset.lock means a concurrent soft reset (which writes
GPU_CMD_SOFT_RESET) could race on the GPU_CMD register.
> - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> - if (ret < 0)
> - goto err_vunmap;
> -
[ ... ]
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + ret = panfrost_perfcnt_hw_enable(pfdev);
[Severity: High]
This is a pre-existing issue, but does panfrost_mmu_as_get() contain an
off-by-one boundary check that could trigger a spurious kernel warning?
Looking at panfrost_mmu_as_get() in drivers/gpu/drm/panfrost/panfrost_mmu.c:
/*
* AS can be retained by active jobs or a perfcnt context,
* hence the '+ 1' here.
*/
WARN_ON(en >= (NUM_JOB_SLOTS + 1));
If a user process submits jobs to all 3 available slots (NUM_JOB_SLOTS = 3)
and simultaneously enables a performance counter session, there are 4
references. atomic_inc_return() returns 4, satisfying 4 >= 4, which
erroneously triggers the WARN_ON.
> + if (ret)
> + goto err_vunmap;
[Severity: Low]
Does mixing goto with scoped_guard() here violate the subsystem cleanup
guidelines?
Jumping to a manual cleanup label from inside a scoped_guard creates
confusing ownership semantics.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828-claude-fixes-v7-0-72a13b2c125d@collabora.com?part=16
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence
2026-08-28 20:56 ` [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
2026-08-28 21:17 ` sashiko-bot
@ 2026-09-01 14:03 ` Boris Brezillon
2026-09-02 15:45 ` Adrián Larumbe
1 sibling, 1 reply; 58+ messages in thread
From: Boris Brezillon @ 2026-09-01 14:03 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Fri, 28 Aug 2026 21:56:56 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Formerly, the reset sequence would race with panfrost_mmu_as_put()
> when tearing down a perfcnt session. On top of that, poking GPU
> registers to program a perfcnt session or obtaining a dump might lead to
> undefined behaviour when done at the same time a reset was ongoing.
>
> Use the reset r/w semaphore to govern access to the hardware at reset
> time. On top of that, expand the DRM uAPI for the perfcnt DUMP operation
> so that userspace can be made aware of a reset having happened, because
> that means counters will go back to 0 and can no longer be accumulated
> to values previously kept in user space.
>
> The new perfcnt-aware reset sequence also takes care to reestablish
> perfcnt to its original configuration if there was an enabled session.
>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 1 +
> drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 189 ++++++++++++++++++++--------
> drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
> include/uapi/drm/panfrost_drm.h | 3 +-
> 4 files changed, 140 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index c8c5dc26b03d..471bd4b037e6 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -498,6 +498,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev)
> panfrost_gpu_power_on(pfdev);
> panfrost_mmu_reset(pfdev);
> panfrost_jm_reset_interrupts(pfdev);
> + panfrost_perfcnt_reset(pfdev);
> }
>
> static int panfrost_device_runtime_resume(struct device *dev)
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> index b3f71d7fd82a..0564aa2753f9 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> @@ -11,6 +11,7 @@
> #include <drm/drm_file.h>
> #include <drm/drm_gem_shmem_helper.h>
> #include <drm/panfrost_drm.h>
> +#include <drm/drm_print.h>
>
> #include "panfrost_device.h"
> #include "panfrost_features.h"
> @@ -25,14 +26,18 @@
> #define BYTES_PER_COUNTER 4
> #define BLOCKS_PER_COREGROUP 8
> #define V4_SHADERS_PER_COREGROUP 4
> +#define PERFCNT_DUMP_MAX_RETRIES 5
>
> struct panfrost_perfcnt {
> struct panfrost_gem_mapping *mapping;
> + unsigned int counterset;
> size_t bosize;
> void *buf;
> struct panfrost_file_priv *user;
> struct mutex lock;
> struct completion dump_comp;
> + bool reset_happened;
> + bool reset_failed;
> };
>
> static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
> @@ -58,25 +63,107 @@ 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);
> + ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> + if (ret < 0)
> + return ret;
> +
> + as = ret;
> + cfg = GPU_PERFCNT_CFG_AS(as) |
> + GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> +
> + /*
> + * Bifrost GPUs have 2 set of counters, but we're only interested by
> + * the first one for now.
> + */
> + if (panfrost_model_is_bifrost(pfdev))
> + cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
> +
> + gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> + gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> + gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> +
> + /*
> + * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
> + * counters.
> + */
> + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
> + else
> + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> +
> + gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
> +
> + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> +
> + return 0;
> +}
> +
> +static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
> + u64 *reset_happened)
> +{
> + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> + u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
> + s64 retries = PERFCNT_DUMP_MAX_RETRIES;
> + bool retry;
> + int ret;
> +
> +dump_retry:
> + retry = false;
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + *reset_happened = perfcnt->reset_happened;
> + perfcnt->reset_happened = false;
> + if (perfcnt->reset_failed) {
I'd rather treat that as a terminal fault (returning -EIO, and maybe
reflecting the perf session as dead through some state flag) in order
to force the user to re-create a session. The only case where it would
fail is if as_get() fails, and it's not supposed to fail after a reset.
This should simplify the dump logic quite a bit.
> + ret = panfrost_perfcnt_hw_enable(pfdev);
> + if (ret)
> + return ret;
> + perfcnt->reset_failed = false;
> + }
> +
> + reinit_completion(&pfdev->perfcnt->dump_comp);
> +
> + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
> + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
> + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
> + GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
> + }
> +
> + /*
> + * Here we release the reset semaphore because perfcnt should not get in the way
> + * of a HW reset. The downside is in the gap between the completion is flagged
> + * by the GPU IRQ handler and the sempahore taken once again, yet another reset
> + * could happen, which would lead to a valid sample being discarded. This is
> + * a solution of compromise between ensuring synchronisation with the reset
> + * sequence and letting it happen as quickly as possible for jobs to be rerun.
> + */
> +
> ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
> msecs_to_jiffies(1000));
> - if (!ret)
> - ret = -ETIMEDOUT;
> - else if (ret > 0)
> - ret = 0;
> +
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + if (ret > 0) {
> + if (perfcnt->reset_happened) {
> + if (--retries >= 0)
> + retry = true;
> + else
> + ret = -EBUSY;
> + } else {
> + ret = 0;
> + }
> + } else if (!ret) {
> + ret = -ETIMEDOUT;
> + }
> + }
I think it's fine to return -EAGAIN here and skip the retry, because
what's important is the re-enable part: get the counting running again
as soon as we can, so that, if the user-side sampling is done at a low
rate, we still get non-zero values between two dumps when a reset
occurred in the middle.
> +
> + if (retry)
> + goto dump_retry;
>
> return ret;
> }
> @@ -87,9 +174,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> {
> struct panfrost_file_priv *user = file_priv->driver_priv;
> struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> - struct iosys_map map;
> struct drm_gem_shmem_object *bo;
> - u32 cfg, as;
> + struct iosys_map map;
> int ret;
>
> if (user == perfcnt->user)
> @@ -122,7 +208,9 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> ret = drm_gem_vmap(&bo->base, &map);
> if (ret)
> goto err_put_mapping;
> +
> perfcnt->buf = map.vaddr;
> + perfcnt->counterset = counterset;
>
> panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
>
> @@ -132,44 +220,19 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
>
> - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> - if (ret < 0)
> - goto err_vunmap;
> -
> - as = ret;
> - cfg = GPU_PERFCNT_CFG_AS(as) |
> - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> -
> - /*
> - * Bifrost GPUs have 2 set of counters, but we're only interested by
> - * the first one for now.
> - */
> - if (panfrost_model_is_bifrost(pfdev))
> - cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
> -
> - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + ret = panfrost_perfcnt_hw_enable(pfdev);
> + if (ret)
> + goto err_vunmap;
>
> - /*
> - * 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);
> + perfcnt->reset_happened = false;
> + perfcnt->reset_failed = false;
> + perfcnt->user = user;
> + }
>
> /* The BO ref is retained by the mapping. */
> drm_gem_object_put(&bo->base);
>
> - perfcnt->user = user;
> -
> return 0;
>
> err_vunmap:
> @@ -195,13 +258,16 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
> if (user != perfcnt->user)
> return -EINVAL;
>
> - panfrost_perfcnt_hw_disable(pfdev);
> + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> + panfrost_perfcnt_hw_disable(pfdev);
> + if (!perfcnt->reset_failed)
> + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
Let's not conflate !reset_failed and owns_as_slot, even if they are
related in practice.
> + perfcnt->user = NULL;
> + }
>
> - perfcnt->user = NULL;
> drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
> perfcnt->buf = NULL;
> panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
> - panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
> panfrost_gem_mapping_put(perfcnt->mapping);
> perfcnt->mapping = NULL;
> pm_runtime_put_autosuspend(pfdev->base.dev);
> @@ -255,7 +321,7 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
> goto out;
> }
>
> - ret = panfrost_perfcnt_dump_locked(pfdev);
> + ret = panfrost_perfcnt_dump_locked(pfdev, &req->hw_reset);
> if (ret)
> goto out;
>
> @@ -338,3 +404,20 @@ void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
> /* Disable everything before leaving. */
> panfrost_perfcnt_hw_disable(pfdev);
> }
> +
> +void panfrost_perfcnt_reset(struct panfrost_device *pfdev)
> +{
> + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> +
> + if (drm_WARN_ON(&pfdev->base, !perfcnt))
> + return;
> +
> + lockdep_assert_held(&pfdev->reset.lock);
> +
> + if (!perfcnt->user)
> + return;
> +
> + perfcnt->reset_failed = !!panfrost_perfcnt_hw_enable(pfdev);
> + perfcnt->reset_happened = true;
> + complete(&perfcnt->dump_comp);
> +}
> diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> index 8bbcf5f5fb33..8b9bc704b634 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> @@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
> struct drm_file *file_priv);
> int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
> struct drm_file *file_priv);
> +void panfrost_perfcnt_reset(struct panfrost_device *pfdev);
>
> #endif
> diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h
> index 50d5337f35ef..3bbf9220103d 100644
> --- a/include/uapi/drm/panfrost_drm.h
> +++ b/include/uapi/drm/panfrost_drm.h
> @@ -47,7 +47,7 @@ extern "C" {
> * them for anything but debugging purpose.
> */
> #define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable)
> -#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
> +#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
>
> #define PANFROST_JD_REQ_FS (1 << 0)
> #define PANFROST_JD_REQ_CYCLE_COUNT (1 << 1)
> @@ -272,6 +272,7 @@ struct drm_panfrost_perfcnt_enable {
>
> struct drm_panfrost_perfcnt_dump {
> __u64 buf_ptr;
> + __u64 hw_reset;
Let's make this a
__u32 state;
__u32 pad;
and define state flags like:
#define PANFROST_PERFCNT_SESSION_DEAD (1 << 0)
#define PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET (1 << 1)
so we can easily extend the thing without introducing new fields or
renaming hw_reset.
> };
>
> /* madvise provides a way to tell the kernel in case a buffers contents
>
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence
2026-09-01 14:03 ` Boris Brezillon
@ 2026-09-02 15:45 ` Adrián Larumbe
2026-09-02 16:51 ` Boris Brezillon
0 siblings, 1 reply; 58+ messages in thread
From: Adrián Larumbe @ 2026-09-02 15:45 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On 01.09.2026 16:03, Boris Brezillon wrote:
> On Fri, 28 Aug 2026 21:56:56 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > Formerly, the reset sequence would race with panfrost_mmu_as_put()
> > when tearing down a perfcnt session. On top of that, poking GPU
> > registers to program a perfcnt session or obtaining a dump might lead to
> > undefined behaviour when done at the same time a reset was ongoing.
> >
> > Use the reset r/w semaphore to govern access to the hardware at reset
> > time. On top of that, expand the DRM uAPI for the perfcnt DUMP operation
> > so that userspace can be made aware of a reset having happened, because
> > that means counters will go back to 0 and can no longer be accumulated
> > to values previously kept in user space.
> >
> > The new perfcnt-aware reset sequence also takes care to reestablish
> > perfcnt to its original configuration if there was an enabled session.
> >
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_device.c | 1 +
> > drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 189 ++++++++++++++++++++--------
> > drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
> > include/uapi/drm/panfrost_drm.h | 3 +-
> > 4 files changed, 140 insertions(+), 54 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > index c8c5dc26b03d..471bd4b037e6 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > @@ -498,6 +498,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev)
> > panfrost_gpu_power_on(pfdev);
> > panfrost_mmu_reset(pfdev);
> > panfrost_jm_reset_interrupts(pfdev);
> > + panfrost_perfcnt_reset(pfdev);
> > }
> >
> > static int panfrost_device_runtime_resume(struct device *dev)
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > index b3f71d7fd82a..0564aa2753f9 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > @@ -11,6 +11,7 @@
> > #include <drm/drm_file.h>
> > #include <drm/drm_gem_shmem_helper.h>
> > #include <drm/panfrost_drm.h>
> > +#include <drm/drm_print.h>
> >
> > #include "panfrost_device.h"
> > #include "panfrost_features.h"
> > @@ -25,14 +26,18 @@
> > #define BYTES_PER_COUNTER 4
> > #define BLOCKS_PER_COREGROUP 8
> > #define V4_SHADERS_PER_COREGROUP 4
> > +#define PERFCNT_DUMP_MAX_RETRIES 5
> >
> > struct panfrost_perfcnt {
> > struct panfrost_gem_mapping *mapping;
> > + unsigned int counterset;
> > size_t bosize;
> > void *buf;
> > struct panfrost_file_priv *user;
> > struct mutex lock;
> > struct completion dump_comp;
> > + bool reset_happened;
> > + bool reset_failed;
> > };
> >
> > static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
> > @@ -58,25 +63,107 @@ 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);
> > + ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> > + if (ret < 0)
> > + return ret;
> > +
> > + as = ret;
> > + cfg = GPU_PERFCNT_CFG_AS(as) |
> > + GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> > +
> > + /*
> > + * Bifrost GPUs have 2 set of counters, but we're only interested by
> > + * the first one for now.
> > + */
> > + if (panfrost_model_is_bifrost(pfdev))
> > + cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
> > +
> > + gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> > + gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> > + gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> > +
> > + /*
> > + * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
> > + * counters.
> > + */
> > + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
> > + else
> > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> > +
> > + gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
> > +
> > + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> > +
> > + return 0;
> > +}
> > +
> > +static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
> > + u64 *reset_happened)
> > +{
> > + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> > + u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
> > + s64 retries = PERFCNT_DUMP_MAX_RETRIES;
> > + bool retry;
> > + int ret;
> > +
> > +dump_retry:
> > + retry = false;
> > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > + *reset_happened = perfcnt->reset_happened;
> > + perfcnt->reset_happened = false;
> > + if (perfcnt->reset_failed) {
>
> I'd rather treat that as a terminal fault (returning -EIO, and maybe
> reflecting the perf session as dead through some state flag) in order
> to force the user to re-create a session. The only case where it would
> fail is if as_get() fails, and it's not supposed to fail after a reset.
> This should simplify the dump logic quite a bit.
Does that mean forcing the user to go through the disable/enable dance?
I thought because in the event of a reset, UM would still like to claim ownership
of perfcnt, recovering within the dump ioctl itself and notifying that a reset happened
so that it can respond to counters also being reset was the quickest way around.
> > + ret = panfrost_perfcnt_hw_enable(pfdev);
> > + if (ret)
> > + return ret;
> > + perfcnt->reset_failed = false;
> > + }
> > +
> > + reinit_completion(&pfdev->perfcnt->dump_comp);
> > +
> > + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
> > + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
> > + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
> > + GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> > + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
> > + }
> > +
> > + /*
> > + * Here we release the reset semaphore because perfcnt should not get in the way
> > + * of a HW reset. The downside is in the gap between the completion is flagged
> > + * by the GPU IRQ handler and the sempahore taken once again, yet another reset
> > + * could happen, which would lead to a valid sample being discarded. This is
> > + * a solution of compromise between ensuring synchronisation with the reset
> > + * sequence and letting it happen as quickly as possible for jobs to be rerun.
> > + */
> > +
> > ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
> > msecs_to_jiffies(1000));
> > - if (!ret)
> > - ret = -ETIMEDOUT;
> > - else if (ret > 0)
> > - ret = 0;
> > +
> > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > + if (ret > 0) {
> > + if (perfcnt->reset_happened) {
> > + if (--retries >= 0)
> > + retry = true;
> > + else
> > + ret = -EBUSY;
> > + } else {
> > + ret = 0;
> > + }
> > + } else if (!ret) {
> > + ret = -ETIMEDOUT;
> > + }
> > + }
>
> I think it's fine to return -EAGAIN here and skip the retry, because
> what's important is the re-enable part: get the counting running again
> as soon as we can, so that, if the user-side sampling is done at a low
> rate, we still get non-zero values between two dumps when a reset
> occurred in the middle.
I'm a bit confused about the uAPI you have in mind. When would we return -EAGAIN and when -EIO?
I guess the former would signal that a dump ioctl can be re-attempted, while the latter that
a whole perfcnt_disable/enable cycle needs to happen before issuing a new dump.
> > +
> > + if (retry)
> > + goto dump_retry;
> >
> > return ret;
> > }
> > @@ -87,9 +174,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > {
> > struct panfrost_file_priv *user = file_priv->driver_priv;
> > struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> > - struct iosys_map map;
> > struct drm_gem_shmem_object *bo;
> > - u32 cfg, as;
> > + struct iosys_map map;
> > int ret;
> >
> > if (user == perfcnt->user)
> > @@ -122,7 +208,9 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > ret = drm_gem_vmap(&bo->base, &map);
> > if (ret)
> > goto err_put_mapping;
> > +
> > perfcnt->buf = map.vaddr;
> > + perfcnt->counterset = counterset;
> >
> > panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
> >
> > @@ -132,44 +220,19 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> > gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
> >
> > - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> > - if (ret < 0)
> > - goto err_vunmap;
> > -
> > - as = ret;
> > - cfg = GPU_PERFCNT_CFG_AS(as) |
> > - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> > -
> > - /*
> > - * Bifrost GPUs have 2 set of counters, but we're only interested by
> > - * the first one for now.
> > - */
> > - if (panfrost_model_is_bifrost(pfdev))
> > - cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
> > -
> > - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> > - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> > - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > + ret = panfrost_perfcnt_hw_enable(pfdev);
> > + if (ret)
> > + goto err_vunmap;
> >
> > - /*
> > - * 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);
> > + perfcnt->reset_happened = false;
> > + perfcnt->reset_failed = false;
> > + perfcnt->user = user;
> > + }
> >
> > /* The BO ref is retained by the mapping. */
> > drm_gem_object_put(&bo->base);
> >
> > - perfcnt->user = user;
> > -
> > return 0;
> >
> > err_vunmap:
> > @@ -195,13 +258,16 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
> > if (user != perfcnt->user)
> > return -EINVAL;
> >
> > - panfrost_perfcnt_hw_disable(pfdev);
> > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > + panfrost_perfcnt_hw_disable(pfdev);
> > + if (!perfcnt->reset_failed)
> > + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
>
> Let's not conflate !reset_failed and owns_as_slot, even if they are
> related in practice.
I did this so that mmu->as_count would be kept balanced in case a reset happened but
panfrost_mmu_as_get() for perfcnt's AS failed during recovery in panfrost_perfcnt_hw_enable().
Because mmu->as_count can only be 0 during perfcnt_disabled when perfcnt_reset has failed,
I thought this conflation was reasonable. Alternatively I could just poke into mmu->as_count
to make sure it's greater than 0, but that sounds like inspecting private MMU implementation
details from the perfcnt subsystem and it doesn't look good.
> > + perfcnt->user = NULL;
> > + }
> >
> > - perfcnt->user = NULL;
> > drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
> > perfcnt->buf = NULL;
> > panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
> > - panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
> > panfrost_gem_mapping_put(perfcnt->mapping);
> > perfcnt->mapping = NULL;
> > pm_runtime_put_autosuspend(pfdev->base.dev);
> > @@ -255,7 +321,7 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
> > goto out;
> > }
> >
> > - ret = panfrost_perfcnt_dump_locked(pfdev);
> > + ret = panfrost_perfcnt_dump_locked(pfdev, &req->hw_reset);
> > if (ret)
> > goto out;
> >
> > @@ -338,3 +404,20 @@ void panfrost_perfcnt_fini(struct panfrost_device *pfdev)
> > /* Disable everything before leaving. */
> > panfrost_perfcnt_hw_disable(pfdev);
> > }
> > +
> > +void panfrost_perfcnt_reset(struct panfrost_device *pfdev)
> > +{
> > + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> > +
> > + if (drm_WARN_ON(&pfdev->base, !perfcnt))
> > + return;
> > +
> > + lockdep_assert_held(&pfdev->reset.lock);
> > +
> > + if (!perfcnt->user)
> > + return;
> > +
> > + perfcnt->reset_failed = !!panfrost_perfcnt_hw_enable(pfdev);
> > + perfcnt->reset_happened = true;
> > + complete(&perfcnt->dump_comp);
> > +}
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> > index 8bbcf5f5fb33..8b9bc704b634 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> > +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h
> > @@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
> > struct drm_file *file_priv);
> > int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
> > struct drm_file *file_priv);
> > +void panfrost_perfcnt_reset(struct panfrost_device *pfdev);
> >
> > #endif
> > diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h
> > index 50d5337f35ef..3bbf9220103d 100644
> > --- a/include/uapi/drm/panfrost_drm.h
> > +++ b/include/uapi/drm/panfrost_drm.h
> > @@ -47,7 +47,7 @@ extern "C" {
> > * them for anything but debugging purpose.
> > */
> > #define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable)
> > -#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
> > +#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump)
> >
> > #define PANFROST_JD_REQ_FS (1 << 0)
> > #define PANFROST_JD_REQ_CYCLE_COUNT (1 << 1)
> > @@ -272,6 +272,7 @@ struct drm_panfrost_perfcnt_enable {
> >
> > struct drm_panfrost_perfcnt_dump {
> > __u64 buf_ptr;
> > + __u64 hw_reset;
>
> Let's make this a
>
> __u32 state;
> __u32 pad;
>
> and define state flags like:
>
> #define PANFROST_PERFCNT_SESSION_DEAD (1 << 0)
> #define PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET (1 << 1)
>
> so we can easily extend the thing without introducing new fields or
> renaming hw_reset.
>
> > };
> >
> > /* madvise provides a way to tell the kernel in case a buffers contents
> >
Adrian Larumbe
^ permalink raw reply [flat|nested] 58+ messages in thread* Re: [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence
2026-09-02 15:45 ` Adrián Larumbe
@ 2026-09-02 16:51 ` Boris Brezillon
0 siblings, 0 replies; 58+ messages in thread
From: Boris Brezillon @ 2026-09-02 16:51 UTC (permalink / raw)
To: Adrián Larumbe
Cc: Rob Herring, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Faith Ekstrand,
Marty E. Plummer, Tomeu Vizoso, Eric Anholt, Alyssa Rosenzweig,
Robin Murphy, Philipp Zabel, dri-devel, linux-kernel,
Collabora Kernel Team, Neil Armstrong
On Wed, 2 Sep 2026 16:45:38 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> On 01.09.2026 16:03, Boris Brezillon wrote:
> > On Fri, 28 Aug 2026 21:56:56 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > Formerly, the reset sequence would race with panfrost_mmu_as_put()
> > > when tearing down a perfcnt session. On top of that, poking GPU
> > > registers to program a perfcnt session or obtaining a dump might lead to
> > > undefined behaviour when done at the same time a reset was ongoing.
> > >
> > > Use the reset r/w semaphore to govern access to the hardware at reset
> > > time. On top of that, expand the DRM uAPI for the perfcnt DUMP operation
> > > so that userspace can be made aware of a reset having happened, because
> > > that means counters will go back to 0 and can no longer be accumulated
> > > to values previously kept in user space.
> > >
> > > The new perfcnt-aware reset sequence also takes care to reestablish
> > > perfcnt to its original configuration if there was an enabled session.
> > >
> > > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > > ---
> > > drivers/gpu/drm/panfrost/panfrost_device.c | 1 +
> > > drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 189 ++++++++++++++++++++--------
> > > drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 1 +
> > > include/uapi/drm/panfrost_drm.h | 3 +-
> > > 4 files changed, 140 insertions(+), 54 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> > > index c8c5dc26b03d..471bd4b037e6 100644
> > > --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> > > +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> > > @@ -498,6 +498,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev)
> > > panfrost_gpu_power_on(pfdev);
> > > panfrost_mmu_reset(pfdev);
> > > panfrost_jm_reset_interrupts(pfdev);
> > > + panfrost_perfcnt_reset(pfdev);
> > > }
> > >
> > > static int panfrost_device_runtime_resume(struct device *dev)
> > > diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > index b3f71d7fd82a..0564aa2753f9 100644
> > > --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
> > > @@ -11,6 +11,7 @@
> > > #include <drm/drm_file.h>
> > > #include <drm/drm_gem_shmem_helper.h>
> > > #include <drm/panfrost_drm.h>
> > > +#include <drm/drm_print.h>
> > >
> > > #include "panfrost_device.h"
> > > #include "panfrost_features.h"
> > > @@ -25,14 +26,18 @@
> > > #define BYTES_PER_COUNTER 4
> > > #define BLOCKS_PER_COREGROUP 8
> > > #define V4_SHADERS_PER_COREGROUP 4
> > > +#define PERFCNT_DUMP_MAX_RETRIES 5
> > >
> > > struct panfrost_perfcnt {
> > > struct panfrost_gem_mapping *mapping;
> > > + unsigned int counterset;
> > > size_t bosize;
> > > void *buf;
> > > struct panfrost_file_priv *user;
> > > struct mutex lock;
> > > struct completion dump_comp;
> > > + bool reset_happened;
> > > + bool reset_failed;
> > > };
> > >
> > > static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev)
> > > @@ -58,25 +63,107 @@ 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);
> > > + ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + as = ret;
> > > + cfg = GPU_PERFCNT_CFG_AS(as) |
> > > + GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> > > +
> > > + /*
> > > + * Bifrost GPUs have 2 set of counters, but we're only interested by
> > > + * the first one for now.
> > > + */
> > > + if (panfrost_model_is_bifrost(pfdev))
> > > + cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset);
> > > +
> > > + gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> > > + gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> > > + gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> > > +
> > > + /*
> > > + * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
> > > + * counters.
> > > + */
> > > + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> > > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0);
> > > + else
> > > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> > > +
> > > + gpu_write(pfdev, GPU_PERFCNT_CFG, cfg);
> > > +
> > > + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186))
> > > + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev,
> > > + u64 *reset_happened)
> > > +{
> > > + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> > > + u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT;
> > > + s64 retries = PERFCNT_DUMP_MAX_RETRIES;
> > > + bool retry;
> > > + int ret;
> > > +
> > > +dump_retry:
> > > + retry = false;
> > > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > > + *reset_happened = perfcnt->reset_happened;
> > > + perfcnt->reset_happened = false;
> > > + if (perfcnt->reset_failed) {
> >
> > I'd rather treat that as a terminal fault (returning -EIO, and maybe
> > reflecting the perf session as dead through some state flag) in order
> > to force the user to re-create a session. The only case where it would
> > fail is if as_get() fails, and it's not supposed to fail after a reset.
> > This should simplify the dump logic quite a bit.
>
> Does that mean forcing the user to go through the disable/enable dance?
So, if we failed to re-enable (which, again, is unlikely if not
impossible), yes, this would force a DISABLE/ENABLE dance.
> I thought because in the event of a reset, UM would still like to claim ownership
> of perfcnt, recovering within the dump ioctl itself and notifying that a reset happened
> so that it can respond to counters also being reset was the quickest way around.
Silently re-enabling and notifying about the RESET disturbing the
counters makes sense, because things are still functional. What I don't
like with this retry approach is the fact it doesn't really solve the
problem because you have a limited amount of attempts, and does add a
fair amount of complexity (take the lock, issue the command, release
the lock, wait, take the lock, check the state, release the lock, retry
if it failed)
>
> > > + ret = panfrost_perfcnt_hw_enable(pfdev);
> > > + if (ret)
> > > + return ret;
> > > + perfcnt->reset_failed = false;
> > > + }
> > > +
> > > + reinit_completion(&pfdev->perfcnt->dump_comp);
> > > +
> > > + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva));
> > > + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva));
> > > + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED |
> > > + GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> > > + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE);
> > > + }
> > > +
> > > + /*
> > > + * Here we release the reset semaphore because perfcnt should not get in the way
> > > + * of a HW reset. The downside is in the gap between the completion is flagged
> > > + * by the GPU IRQ handler and the sempahore taken once again, yet another reset
> > > + * could happen, which would lead to a valid sample being discarded. This is
> > > + * a solution of compromise between ensuring synchronisation with the reset
> > > + * sequence and letting it happen as quickly as possible for jobs to be rerun.
> > > + */
> > > +
> > > ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp,
> > > msecs_to_jiffies(1000));
> > > - if (!ret)
> > > - ret = -ETIMEDOUT;
> > > - else if (ret > 0)
> > > - ret = 0;
> > > +
> > > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > > + if (ret > 0) {
> > > + if (perfcnt->reset_happened) {
> > > + if (--retries >= 0)
> > > + retry = true;
> > > + else
> > > + ret = -EBUSY;
> > > + } else {
> > > + ret = 0;
> > > + }
> > > + } else if (!ret) {
> > > + ret = -ETIMEDOUT;
> > > + }
> > > + }
> >
> > I think it's fine to return -EAGAIN here and skip the retry, because
> > what's important is the re-enable part: get the counting running again
> > as soon as we can, so that, if the user-side sampling is done at a low
> > rate, we still get non-zero values between two dumps when a reset
> > occurred in the middle.
>
> I'm a bit confused about the uAPI you have in mind. When would we return -EAGAIN and when -EIO?
So, EAGAIN is if your SAMPLE request is interrupted by a RESET, but
counters were properly re-enabled after the RESET. This basically gets
rid of the retry logic kernel side and leaves that to the UMD.
EIO if when the re-enable failed.
> I guess the former would signal that a dump ioctl can be re-attempted, while the latter that
> a whole perfcnt_disable/enable cycle needs to happen before issuing a new dump.
Yep, this.
>
> > > +
> > > + if (retry)
> > > + goto dump_retry;
> > >
> > > return ret;
> > > }
> > > @@ -87,9 +174,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > > {
> > > struct panfrost_file_priv *user = file_priv->driver_priv;
> > > struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
> > > - struct iosys_map map;
> > > struct drm_gem_shmem_object *bo;
> > > - u32 cfg, as;
> > > + struct iosys_map map;
> > > int ret;
> > >
> > > if (user == perfcnt->user)
> > > @@ -122,7 +208,9 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > > ret = drm_gem_vmap(&bo->base, &map);
> > > if (ret)
> > > goto err_put_mapping;
> > > +
> > > perfcnt->buf = map.vaddr;
> > > + perfcnt->counterset = counterset;
> > >
> > > panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer");
> > >
> > > @@ -132,44 +220,19 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
> > > gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED);
> > > gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR);
> > >
> > > - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
> > > - if (ret < 0)
> > > - goto err_vunmap;
> > > -
> > > - as = ret;
> > > - cfg = GPU_PERFCNT_CFG_AS(as) |
> > > - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
> > > -
> > > - /*
> > > - * Bifrost GPUs have 2 set of counters, but we're only interested by
> > > - * the first one for now.
> > > - */
> > > - if (panfrost_model_is_bifrost(pfdev))
> > > - cfg |= GPU_PERFCNT_CFG_SETSEL(counterset);
> > > -
> > > - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff);
> > > - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff);
> > > - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff);
> > > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > > + ret = panfrost_perfcnt_hw_enable(pfdev);
> > > + if (ret)
> > > + goto err_vunmap;
> > >
> > > - /*
> > > - * 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);
> > > + perfcnt->reset_happened = false;
> > > + perfcnt->reset_failed = false;
> > > + perfcnt->user = user;
> > > + }
> > >
> > > /* The BO ref is retained by the mapping. */
> > > drm_gem_object_put(&bo->base);
> > >
> > > - perfcnt->user = user;
> > > -
> > > return 0;
> > >
> > > err_vunmap:
> > > @@ -195,13 +258,16 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
> > > if (user != perfcnt->user)
> > > return -EINVAL;
> > >
> > > - panfrost_perfcnt_hw_disable(pfdev);
> > > + scoped_guard(rwsem_read, &pfdev->reset.lock) {
> > > + panfrost_perfcnt_hw_disable(pfdev);
> > > + if (!perfcnt->reset_failed)
> > > + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
> >
> > Let's not conflate !reset_failed and owns_as_slot, even if they are
> > related in practice.
>
> I did this so that mmu->as_count would be kept balanced in case a reset happened but
> panfrost_mmu_as_get() for perfcnt's AS failed during recovery in panfrost_perfcnt_hw_enable().
> Because mmu->as_count can only be 0 during perfcnt_disabled when perfcnt_reset has failed,
> I thought this conflation was reasonable. Alternatively I could just poke into mmu->as_count
> to make sure it's greater than 0, but that sounds like inspecting private MMU implementation
> details from the perfcnt subsystem and it doesn't look good.
No, I'm actually suggesting keeping track of this information through a
dedicated field. Can be:
// >=0 => owns an AS ref
int as_id;
or:
// true when the perfcnt session owns an AS ref
bool owns_as_ref;
and you set/reset that field in the enable/disable/reset path.
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH v7 17/17] drm/panfrost: Bump driver minor to reflect new DUMP IOCTL req field
2026-08-28 20:56 [PATCH v7 00/17] Collection of fixes for Panfrost: Perfcnt, RPM, refactorings Adrián Larumbe
` (15 preceding siblings ...)
2026-08-28 20:56 ` [PATCH v7 16/17] drm/panfrost: Fix races between perfcnt and reset sequence Adrián Larumbe
@ 2026-08-28 20:56 ` Adrián Larumbe
16 siblings, 0 replies; 58+ messages in thread
From: Adrián Larumbe @ 2026-08-28 20:56 UTC (permalink / raw)
To: Boris Brezillon, Rob Herring, Steven Price, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Faith Ekstrand, Marty E. Plummer, Tomeu Vizoso, Eric Anholt,
Alyssa Rosenzweig, Robin Murphy, Philipp Zabel
Cc: dri-devel, linux-kernel, Collabora Kernel Team,
Adrián Larumbe, Neil Armstrong
Expose expanded PERFCNT_DUMP IOCTL interface to user mode.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
drivers/gpu/drm/panfrost/panfrost_drv.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 571a26b84126..7869a0cfc4c3 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -808,6 +808,7 @@ static const struct file_operations panfrost_drm_driver_fops = {
* - 1.6 - adds PANFROST_BO_MAP_WB, PANFROST_IOCTL_SYNC_BO,
* PANFROST_IOCTL_QUERY_BO_INFO and
* DRM_PANFROST_PARAM_SELECTED_COHERENCY
+ * - 1.7 - adds PERFCNT_DUMP req hw_reset field
*/
static const struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
@@ -820,7 +821,7 @@ static const struct drm_driver panfrost_drm_driver = {
.name = "panfrost",
.desc = "panfrost DRM",
.major = 1,
- .minor = 6,
+ .minor = 7,
.gem_create_object = panfrost_gem_create_object,
.gem_prime_import = panfrost_gem_prime_import,
--
2.55.0
^ permalink raw reply related [flat|nested] 58+ messages in thread