[PATCH v2 4/4] drm/panfrost: Display list of device JM contexts over debugfs

Adrián Larumbe posted 4 patches 4 weeks, 1 day ago
There is a newer version of this series
[PATCH v2 4/4] drm/panfrost: Display list of device JM contexts over debugfs
Posted by Adrián Larumbe 4 weeks, 1 day ago
From: Boris Brezillon <boris.brezillon@collabora.com>

For DebugFS builds, create a filesystem knob that, for every single open
file of the Panfrost DRM device, shows its command name information and
PID (when applicable), and all of its existing JM contexts.

For every context, show its priority and job config.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
---
 drivers/gpu/drm/panfrost/panfrost_drv.c | 97 +++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 02f704ec4961..b3d14b887da4 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -712,6 +712,48 @@ static int panthor_gems_show(struct seq_file *m, void *data)
 	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 = NULL;
+
+	static const char * const prios[] = {
+		[DRM_SCHED_PRIORITY_HIGH] = "HIGH",
+		[DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
+		[DRM_SCHED_PRIORITY_LOW] = "LOW",
+	};
+
+	if (jm_ctx->slots[0].sched_entity.priority !=
+	    jm_ctx->slots[1].sched_entity.priority)
+		drm_warn(ddev, "Slot priorities should be the same in a single context");
+
+	if (jm_ctx->slots[0].sched_entity.priority < ARRAY_SIZE(prios))
+		prio = prios[jm_ctx->slots[0].sched_entity.priority];
+
+	seq_printf(m, " JM context %u: priority %s config %x\n",
+		   handle, prio ? prio : "UNKNOWN", jm_ctx->config);
+}
+
+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},
 };
@@ -725,9 +767,64 @@ static int panthor_gems_debugfs_init(struct drm_minor *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
 
-- 
2.50.0

Re: [PATCH v2 4/4] drm/panfrost: Display list of device JM contexts over debugfs
Posted by Steven Price 3 weeks, 1 day ago
On 04/09/2025 01:08, Adrián Larumbe wrote:
> From: Boris Brezillon <boris.brezillon@collabora.com>
> 
> For DebugFS builds, create a filesystem knob that, for every single open
> file of the Panfrost DRM device, shows its command name information and
> PID (when applicable), and all of its existing JM contexts.
> 
> For every context, show its priority and job config.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> ---
>  drivers/gpu/drm/panfrost/panfrost_drv.c | 97 +++++++++++++++++++++++++
>  1 file changed, 97 insertions(+)
> 
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 02f704ec4961..b3d14b887da4 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> @@ -712,6 +712,48 @@ static int panthor_gems_show(struct seq_file *m, void *data)
>  	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 = NULL;
> +
> +	static const char * const prios[] = {
> +		[DRM_SCHED_PRIORITY_HIGH] = "HIGH",
> +		[DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
> +		[DRM_SCHED_PRIORITY_LOW] = "LOW",
> +	};
> +
> +	if (jm_ctx->slots[0].sched_entity.priority !=
> +	    jm_ctx->slots[1].sched_entity.priority)
> +		drm_warn(ddev, "Slot priorities should be the same in a single context");
> +
> +	if (jm_ctx->slots[0].sched_entity.priority < ARRAY_SIZE(prios))
> +		prio = prios[jm_ctx->slots[0].sched_entity.priority];
> +
> +	seq_printf(m, " JM context %u: priority %s config %x\n",
> +		   handle, prio ? prio : "UNKNOWN", jm_ctx->config);

NIT: If you assign prio to "UNKNOWN" to begin with (rather than NULL)
you can avoid this ?: operator.

> +}
> +
> +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);

Is it so bad if we just held the xa lock for the whole loop? It just
seems unnecessarily complex.

Thanks,
Steve

> +
> +	return 0;
> +}
> +
>  static struct drm_info_list panthor_debugfs_list[] = {
>  	{"gems", panthor_gems_show, 0, NULL},
>  };
> @@ -725,9 +767,64 @@ static int panthor_gems_debugfs_init(struct drm_minor *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
>  

Re: [PATCH v2 4/4] drm/panfrost: Display list of device JM contexts over debugfs
Posted by Adrián Larumbe 3 weeks ago
On 10.09.2025 16:42, Steven Price wrote:
> On 04/09/2025 01:08, Adrián Larumbe wrote:
> > From: Boris Brezillon <boris.brezillon@collabora.com>
> >
> > For DebugFS builds, create a filesystem knob that, for every single open
> > file of the Panfrost DRM device, shows its command name information and
> > PID (when applicable), and all of its existing JM contexts.
> >
> > For every context, show its priority and job config.
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
> > ---
> >  drivers/gpu/drm/panfrost/panfrost_drv.c | 97 +++++++++++++++++++++++++
> >  1 file changed, 97 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > index 02f704ec4961..b3d14b887da4 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> > @@ -712,6 +712,48 @@ static int panthor_gems_show(struct seq_file *m, void *data)
> >  	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 = NULL;
> > +
> > +	static const char * const prios[] = {
> > +		[DRM_SCHED_PRIORITY_HIGH] = "HIGH",
> > +		[DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
> > +		[DRM_SCHED_PRIORITY_LOW] = "LOW",
> > +	};
> > +
> > +	if (jm_ctx->slots[0].sched_entity.priority !=
> > +	    jm_ctx->slots[1].sched_entity.priority)
> > +		drm_warn(ddev, "Slot priorities should be the same in a single context");
> > +
> > +	if (jm_ctx->slots[0].sched_entity.priority < ARRAY_SIZE(prios))
> > +		prio = prios[jm_ctx->slots[0].sched_entity.priority];
> > +
> > +	seq_printf(m, " JM context %u: priority %s config %x\n",
> > +		   handle, prio ? prio : "UNKNOWN", jm_ctx->config);
>
> NIT: If you assign prio to "UNKNOWN" to begin with (rather than NULL)
> you can avoid this ?: operator.

Acked.

> > +}
> > +
> > +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);
>
> Is it so bad if we just held the xa lock for the whole loop? It just
> seems unnecessarily complex.

xa_unlock() is defined as a spinlock which are fast. I'm often of the view that the
critical region should be as narrow as possible, especially when debug code is clashing
with the normal operation of the driver.

> Thanks,
> Steve
>
> > +
> > +	return 0;
> > +}
> > +
> >  static struct drm_info_list panthor_debugfs_list[] = {
> >  	{"gems", panthor_gems_show, 0, NULL},
> >  };
> > @@ -725,9 +767,64 @@ static int panthor_gems_debugfs_init(struct drm_minor *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
> >

Adrian Larumbe