All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/2] drm/panthor: Scheduler fixes for termination failure and timeouts
@ 2025-11-12 12:17 Boris Brezillon
  2025-11-12 12:17 ` [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job Boris Brezillon
  2025-11-12 12:17 ` [PATCH v7 2/2] drm/panthor: Reset queue slots if termination fails Boris Brezillon
  0 siblings, 2 replies; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 12:17 UTC (permalink / raw)
  To: Boris Brezillon, Steven Price, Liviu Dudau, Adrián Larumbe
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, dri-devel,
	Detlev Casanova, Ashley Smith, kernel

This patch series includes previously attempted patches to fix panthor
scheduler issues with spurious timeouts and issues when a termination
failed which would lead to a race condition.

Timeout recovery has been tested with some IGT tests issuing jobs with
infinite loops [1]. It's certainly not enough to claim that everything
works as it should, but that's still more testing than we had so far
;-).

[1]https://gitlab.freedesktop.org/bbrezillon/igt-gpu-tools/-/commit/15c3ee220808a437a76638bd21fedfb4498a434f

Changes in v7:
 - Add Steve's R-b
 - Use the local group variable when we can

Changes in v6:
 - Re-order changes
 - Dropped the Fixes tag on one patch
 - Cover UAF situation when the timeout work is pending/running at group
   destruction time
Changes in v5:
 - Swiched to a patch series to make sure the patch which addresses the
   bug is added as a requirement on the scheduler patch.
Changes in v4:
 - Moved code related to a timeout bug to a separate patch as this
   was not relevant to this change.
Changes in v3:
 - Moved to a patch series to make sure this bug fix happens before the
   changes to the scheduler
Changes in v2:
 - Fixed syntax error
*** BLURB HERE ***

Ashley Smith (2):
  drm/panthor: Make the timeout per-queue instead of per-job
  drm/panthor: Reset queue slots if termination fails

 drivers/gpu/drm/panthor/panthor_sched.c | 299 +++++++++++++++++-------
 1 file changed, 218 insertions(+), 81 deletions(-)

-- 
2.51.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 12:17 [PATCH v7 0/2] drm/panthor: Scheduler fixes for termination failure and timeouts Boris Brezillon
@ 2025-11-12 12:17 ` Boris Brezillon
  2025-11-12 12:38   ` Philipp Stanner
  2025-11-12 12:17 ` [PATCH v7 2/2] drm/panthor: Reset queue slots if termination fails Boris Brezillon
  1 sibling, 1 reply; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 12:17 UTC (permalink / raw)
  To: Boris Brezillon, Steven Price, Liviu Dudau, Adrián Larumbe
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, dri-devel,
	Detlev Casanova, Ashley Smith, kernel

From: Ashley Smith <ashley.smith@collabora.com>

The timeout logic provided by drm_sched leads to races when we try
to suspend it while the drm_sched workqueue queues more jobs. Let's
overhaul the timeout handling in panthor to have our own delayed work
that's resumed/suspended when a group is resumed/suspended. When an
actual timeout occurs, we call drm_sched_fault() to report it
through drm_sched, still. But otherwise, the drm_sched timeout is
disabled (set to MAX_SCHEDULE_TIMEOUT), which leaves us in control of
how we protect modifications on the timer.

One issue seems to be when we call drm_sched_suspend_timeout() from
both queue_run_job() and tick_work() which could lead to races due to
drm_sched_suspend_timeout() not having a lock. Another issue seems to
be in queue_run_job() if the group is not scheduled, we suspend the
timeout again which undoes what drm_sched_job_begin() did when calling
drm_sched_start_timeout(). So the timeout does not reset when a job
is finished.

v2:
- Fix syntax error

v3:
- Split the changes in two commits

v4:
- No changes

v5:
- No changes

v6:
- Fix a NULL deref in group_can_run(), and narrow the group variable
  scope to avoid such mistakes in the future
- Add an queue_timeout_is_suspended() helper to clarify things

Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block")
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Signed-off-by: Ashley Smith <ashley.smith@collabora.com>
Co-developed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 drivers/gpu/drm/panthor/panthor_sched.c | 285 +++++++++++++++++-------
 1 file changed, 206 insertions(+), 79 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index e74ca071159d..b440187798dd 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -364,17 +364,20 @@ struct panthor_queue {
 	/** @name: DRM scheduler name for this queue. */
 	char *name;
 
-	/**
-	 * @remaining_time: Time remaining before the job timeout expires.
-	 *
-	 * The job timeout is suspended when the queue is not scheduled by the
-	 * FW. Every time we suspend the timer, we need to save the remaining
-	 * time so we can restore it later on.
-	 */
-	unsigned long remaining_time;
+	/** @timeout: Queue timeout related fields. */
+	struct {
+		/** @timeout.work: Work executed when a queue timeout occurs. */
+		struct delayed_work work;
 
-	/** @timeout_suspended: True if the job timeout was suspended. */
-	bool timeout_suspended;
+		/**
+		 * @timeout.remaining: Time remaining before a queue timeout.
+		 *
+		 * When the timer is running, this value is set to MAX_SCHEDULE_TIMEOUT.
+		 * When the timer is suspended, it's set to the time remaining when the
+		 * timer was suspended.
+		 */
+		unsigned long remaining;
+	} timeout;
 
 	/**
 	 * @doorbell_id: Doorbell assigned to this queue.
@@ -899,6 +902,10 @@ static void group_free_queue(struct panthor_group *group, struct panthor_queue *
 	if (IS_ERR_OR_NULL(queue))
 		return;
 
+	/* This should have been disabled before that point. */
+	drm_WARN_ON(&group->ptdev->base,
+		    disable_delayed_work_sync(&queue->timeout.work));
+
 	if (queue->entity.fence_context)
 		drm_sched_entity_destroy(&queue->entity);
 
@@ -1046,6 +1053,115 @@ group_unbind_locked(struct panthor_group *group)
 	return 0;
 }
 
+static bool
+group_is_idle(struct panthor_group *group)
+{
+	struct panthor_device *ptdev = group->ptdev;
+	u32 inactive_queues;
+
+	if (group->csg_id >= 0)
+		return ptdev->scheduler->csg_slots[group->csg_id].idle;
+
+	inactive_queues = group->idle_queues | group->blocked_queues;
+	return hweight32(inactive_queues) == group->queue_count;
+}
+
+static void
+queue_reset_timeout_locked(struct panthor_queue *queue)
+{
+	lockdep_assert_held(&queue->fence_ctx.lock);
+
+	if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
+		mod_delayed_work(queue->scheduler.timeout_wq,
+				 &queue->timeout.work,
+				 msecs_to_jiffies(JOB_TIMEOUT_MS));
+	}
+}
+
+static bool
+group_can_run(struct panthor_group *group)
+{
+	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
+	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
+	       !group->destroyed && group->fatal_queues == 0 &&
+	       !group->timedout;
+}
+
+static bool
+queue_timeout_is_suspended(struct panthor_queue *queue)
+{
+	/* When running, the remaining time is set to MAX_SCHEDULE_TIMEOUT. */
+	return queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT;
+}
+
+static void
+queue_suspend_timeout_locked(struct panthor_queue *queue)
+{
+	unsigned long qtimeout, now;
+	struct panthor_group *group;
+	struct panthor_job *job;
+	bool timer_was_active;
+
+	lockdep_assert_held(&queue->fence_ctx.lock);
+
+	/* Already suspended, nothing to do. */
+	if (queue_timeout_is_suspended(queue))
+		return;
+
+	job = list_first_entry_or_null(&queue->fence_ctx.in_flight_jobs,
+				       struct panthor_job, node);
+	group = job ? job->group : NULL;
+
+	/* If the queue is blocked and the group is idle, we want the timer to
+	 * keep running because the group can't be unblocked by other queues,
+	 * so it has to come from an external source, and we want to timebox
+	 * this external signalling.
+	 */
+	if (group && group_can_run(group) &&
+	    (group->blocked_queues & BIT(job->queue_idx)) &&
+	    group_is_idle(group))
+		return;
+
+	now = jiffies;
+	qtimeout = queue->timeout.work.timer.expires;
+
+	/* Cancel the timer. */
+	timer_was_active = cancel_delayed_work(&queue->timeout.work);
+	if (!timer_was_active || !job)
+		queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+	else if (time_after(qtimeout, now))
+		queue->timeout.remaining = qtimeout - now;
+	else
+		queue->timeout.remaining = 0;
+
+	if (WARN_ON_ONCE(queue->timeout.remaining > msecs_to_jiffies(JOB_TIMEOUT_MS)))
+		queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+}
+
+static void
+queue_suspend_timeout(struct panthor_queue *queue)
+{
+	spin_lock(&queue->fence_ctx.lock);
+	queue_suspend_timeout_locked(queue);
+	spin_unlock(&queue->fence_ctx.lock);
+}
+
+static void
+queue_resume_timeout(struct panthor_queue *queue)
+{
+	spin_lock(&queue->fence_ctx.lock);
+
+	if (queue_timeout_is_suspended(queue)) {
+		mod_delayed_work(queue->scheduler.timeout_wq,
+				 &queue->timeout.work,
+				 queue->timeout.remaining);
+
+		queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
+	}
+
+	spin_unlock(&queue->fence_ctx.lock);
+}
+
 /**
  * cs_slot_prog_locked() - Program a queue slot
  * @ptdev: Device.
@@ -1084,10 +1200,8 @@ cs_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
 			       CS_IDLE_EMPTY |
 			       CS_STATE_MASK |
 			       CS_EXTRACT_EVENT);
-	if (queue->iface.input->insert != queue->iface.input->extract && queue->timeout_suspended) {
-		drm_sched_resume_timeout(&queue->scheduler, queue->remaining_time);
-		queue->timeout_suspended = false;
-	}
+	if (queue->iface.input->insert != queue->iface.input->extract)
+		queue_resume_timeout(queue);
 }
 
 /**
@@ -1114,14 +1228,7 @@ cs_slot_reset_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
 			       CS_STATE_STOP,
 			       CS_STATE_MASK);
 
-	/* If the queue is blocked, we want to keep the timeout running, so
-	 * we can detect unbounded waits and kill the group when that happens.
-	 */
-	if (!(group->blocked_queues & BIT(cs_id)) && !queue->timeout_suspended) {
-		queue->remaining_time = drm_sched_suspend_timeout(&queue->scheduler);
-		queue->timeout_suspended = true;
-		WARN_ON(queue->remaining_time > msecs_to_jiffies(JOB_TIMEOUT_MS));
-	}
+	queue_suspend_timeout(queue);
 
 	return 0;
 }
@@ -1916,28 +2023,6 @@ tick_ctx_is_full(const struct panthor_scheduler *sched,
 	return ctx->group_count == sched->csg_slot_count;
 }
 
-static bool
-group_is_idle(struct panthor_group *group)
-{
-	struct panthor_device *ptdev = group->ptdev;
-	u32 inactive_queues;
-
-	if (group->csg_id >= 0)
-		return ptdev->scheduler->csg_slots[group->csg_id].idle;
-
-	inactive_queues = group->idle_queues | group->blocked_queues;
-	return hweight32(inactive_queues) == group->queue_count;
-}
-
-static bool
-group_can_run(struct panthor_group *group)
-{
-	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
-	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
-	       !group->destroyed && group->fatal_queues == 0 &&
-	       !group->timedout;
-}
-
 static void
 tick_ctx_pick_groups_from_list(const struct panthor_scheduler *sched,
 			       struct panthor_sched_tick_ctx *ctx,
@@ -2619,6 +2704,7 @@ static void group_schedule_locked(struct panthor_group *group, u32 queue_mask)
 static void queue_stop(struct panthor_queue *queue,
 		       struct panthor_job *bad_job)
 {
+	disable_delayed_work_sync(&queue->timeout.work);
 	drm_sched_stop(&queue->scheduler, bad_job ? &bad_job->base : NULL);
 }
 
@@ -2630,6 +2716,7 @@ static void queue_start(struct panthor_queue *queue)
 	list_for_each_entry(job, &queue->scheduler.pending_list, base.list)
 		job->base.s_fence->parent = dma_fence_get(job->done_fence);
 
+	enable_delayed_work(&queue->timeout.work);
 	drm_sched_start(&queue->scheduler, 0);
 }
 
@@ -2696,7 +2783,6 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
 {
 	struct panthor_scheduler *sched = ptdev->scheduler;
 	struct panthor_csg_slots_upd_ctx upd_ctx;
-	struct panthor_group *group;
 	u32 suspended_slots;
 	u32 i;
 
@@ -2786,8 +2872,8 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
 
 	for (i = 0; i < sched->csg_slot_count; i++) {
 		struct panthor_csg_slot *csg_slot = &sched->csg_slots[i];
+		struct panthor_group *group = csg_slot->group;
 
-		group = csg_slot->group;
 		if (!group)
 			continue;
 
@@ -2916,35 +3002,47 @@ void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
 	xa_unlock(&gpool->xa);
 }
 
-static void group_sync_upd_work(struct work_struct *work)
+static bool queue_check_job_completion(struct panthor_queue *queue)
 {
-	struct panthor_group *group =
-		container_of(work, struct panthor_group, sync_upd_work);
+	struct panthor_syncobj_64b *syncobj = NULL;
 	struct panthor_job *job, *job_tmp;
+	bool cookie, progress = false;
 	LIST_HEAD(done_jobs);
-	u32 queue_idx;
-	bool cookie;
 
 	cookie = dma_fence_begin_signalling();
-	for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
-		struct panthor_queue *queue = group->queues[queue_idx];
-		struct panthor_syncobj_64b *syncobj;
+	spin_lock(&queue->fence_ctx.lock);
+	list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
+		if (!syncobj) {
+			struct panthor_group *group = job->group;
 
-		if (!queue)
-			continue;
-
-		syncobj = group->syncobjs->kmap + (queue_idx * sizeof(*syncobj));
-
-		spin_lock(&queue->fence_ctx.lock);
-		list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
-			if (syncobj->seqno < job->done_fence->seqno)
-				break;
-
-			list_move_tail(&job->node, &done_jobs);
-			dma_fence_signal_locked(job->done_fence);
+			syncobj = group->syncobjs->kmap +
+				  (job->queue_idx * sizeof(*syncobj));
 		}
-		spin_unlock(&queue->fence_ctx.lock);
+
+		if (syncobj->seqno < job->done_fence->seqno)
+			break;
+
+		list_move_tail(&job->node, &done_jobs);
+		dma_fence_signal_locked(job->done_fence);
 	}
+
+	if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
+		/* If we have no job left, we cancel the timer, and reset remaining
+		 * time to its default so it can be restarted next time
+		 * queue_resume_timeout() is called.
+		 */
+		queue_suspend_timeout_locked(queue);
+
+		/* If there's no job pending, we consider it progress to avoid a
+		 * spurious timeout if the timeout handler and the sync update
+		 * handler raced.
+		 */
+		progress = true;
+	} else if (!list_empty(&done_jobs)) {
+		queue_reset_timeout_locked(queue);
+		progress = true;
+	}
+	spin_unlock(&queue->fence_ctx.lock);
 	dma_fence_end_signalling(cookie);
 
 	list_for_each_entry_safe(job, job_tmp, &done_jobs, node) {
@@ -2954,6 +3052,27 @@ static void group_sync_upd_work(struct work_struct *work)
 		panthor_job_put(&job->base);
 	}
 
+	return progress;
+}
+
+static void group_sync_upd_work(struct work_struct *work)
+{
+	struct panthor_group *group =
+		container_of(work, struct panthor_group, sync_upd_work);
+	u32 queue_idx;
+	bool cookie;
+
+	cookie = dma_fence_begin_signalling();
+	for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
+		struct panthor_queue *queue = group->queues[queue_idx];
+
+		if (!queue)
+			continue;
+
+		queue_check_job_completion(queue);
+	}
+	dma_fence_end_signalling(cookie);
+
 	group_put(group);
 }
 
@@ -3201,17 +3320,6 @@ queue_run_job(struct drm_sched_job *sched_job)
 	queue->iface.input->insert = job->ringbuf.end;
 
 	if (group->csg_id < 0) {
-		/* If the queue is blocked, we want to keep the timeout running, so we
-		 * can detect unbounded waits and kill the group when that happens.
-		 * Otherwise, we suspend the timeout so the time we spend waiting for
-		 * a CSG slot is not counted.
-		 */
-		if (!(group->blocked_queues & BIT(job->queue_idx)) &&
-		    !queue->timeout_suspended) {
-			queue->remaining_time = drm_sched_suspend_timeout(&queue->scheduler);
-			queue->timeout_suspended = true;
-		}
-
 		group_schedule_locked(group, BIT(job->queue_idx));
 	} else {
 		gpu_write(ptdev, CSF_DOORBELL(queue->doorbell_id), 1);
@@ -3220,6 +3328,7 @@ queue_run_job(struct drm_sched_job *sched_job)
 			pm_runtime_get(ptdev->base.dev);
 			sched->pm.has_ref = true;
 		}
+		queue_resume_timeout(queue);
 		panthor_devfreq_record_busy(sched->ptdev);
 	}
 
@@ -3270,6 +3379,11 @@ queue_timedout_job(struct drm_sched_job *sched_job)
 
 	queue_start(queue);
 
+	/* We already flagged the queue as faulty, make sure we don't get
+	 * called again.
+	 */
+	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
+
 	return DRM_GPU_SCHED_STAT_RESET;
 }
 
@@ -3312,6 +3426,17 @@ static u32 calc_profiling_ringbuf_num_slots(struct panthor_device *ptdev,
 	return DIV_ROUND_UP(cs_ringbuf_size, min_profiled_job_instrs * sizeof(u64));
 }
 
+static void queue_timeout_work(struct work_struct *work)
+{
+	struct panthor_queue *queue = container_of(work, struct panthor_queue,
+						   timeout.work.work);
+	bool progress;
+
+	progress = queue_check_job_completion(queue);
+	if (!progress)
+		drm_sched_fault(&queue->scheduler);
+}
+
 static struct panthor_queue *
 group_create_queue(struct panthor_group *group,
 		   const struct drm_panthor_queue_create *args,
@@ -3328,7 +3453,7 @@ group_create_queue(struct panthor_group *group,
 		 * their profiling status.
 		 */
 		.credit_limit = args->ringbuf_size / sizeof(u64),
-		.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
+		.timeout = MAX_SCHEDULE_TIMEOUT,
 		.timeout_wq = group->ptdev->reset.wq,
 		.dev = group->ptdev->base.dev,
 	};
@@ -3350,6 +3475,8 @@ group_create_queue(struct panthor_group *group,
 	if (!queue)
 		return ERR_PTR(-ENOMEM);
 
+	queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+	INIT_DELAYED_WORK(&queue->timeout.work, queue_timeout_work);
 	queue->fence_ctx.id = dma_fence_context_alloc(1);
 	spin_lock_init(&queue->fence_ctx.lock);
 	INIT_LIST_HEAD(&queue->fence_ctx.in_flight_jobs);
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v7 2/2] drm/panthor: Reset queue slots if termination fails
  2025-11-12 12:17 [PATCH v7 0/2] drm/panthor: Scheduler fixes for termination failure and timeouts Boris Brezillon
  2025-11-12 12:17 ` [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job Boris Brezillon
@ 2025-11-12 12:17 ` Boris Brezillon
  1 sibling, 0 replies; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 12:17 UTC (permalink / raw)
  To: Boris Brezillon, Steven Price, Liviu Dudau, Adrián Larumbe
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, dri-devel,
	Detlev Casanova, Ashley Smith, kernel

From: Ashley Smith <ashley.smith@collabora.com>

Make sure the queue slot is reset even if we failed termination so
we don't have garbage in the CS input interface after a reset. In
practice that's not a problem because we zero out all RW sections when
a hangs occurs, but it's safer to reset things manually, in case we
decide to not conditionally reload RW sections based on the type of
hang.

v4:
- Split the changes in two separate patches

v5:
- No changes

v6:
- Adjust the explanation in the commit message
- Drop the Fixes tag
- Put after the timeout changes and make the two patches independent
  so one can be backported, and the other not

v7:
- Use the local group variable instead of dereferencing csg_slot->group
- Add Steve's R-b

Signed-off-by: Ashley Smith <ashley.smith@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 drivers/gpu/drm/panthor/panthor_sched.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index b440187798dd..36c75c39e3e4 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -2836,13 +2836,23 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
 		while (slot_mask) {
 			u32 csg_id = ffs(slot_mask) - 1;
 			struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
+			struct panthor_group *group = csg_slot->group;
 
 			/* Terminate command timedout, but the soft-reset will
 			 * automatically terminate all active groups, so let's
 			 * force the state to halted here.
 			 */
-			if (csg_slot->group->state != PANTHOR_CS_GROUP_TERMINATED)
-				csg_slot->group->state = PANTHOR_CS_GROUP_TERMINATED;
+			if (group->state != PANTHOR_CS_GROUP_TERMINATED) {
+				group->state = PANTHOR_CS_GROUP_TERMINATED;
+
+				/* Reset the queue slots manually if the termination
+				 * request failed.
+				 */
+				for (i = 0; i < group->queue_count; i++) {
+					if (group->queues[i])
+						cs_slot_reset_locked(ptdev, csg_id, i);
+				}
+			}
 			slot_mask &= ~BIT(csg_id);
 		}
 	}
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 12:17 ` [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job Boris Brezillon
@ 2025-11-12 12:38   ` Philipp Stanner
  2025-11-12 13:31     ` Boris Brezillon
  0 siblings, 1 reply; 10+ messages in thread
From: Philipp Stanner @ 2025-11-12 12:38 UTC (permalink / raw)
  To: Boris Brezillon, Steven Price, Liviu Dudau, Adrián Larumbe
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, dri-devel,
	Detlev Casanova, Ashley Smith, kernel

On Wed, 2025-11-12 at 13:17 +0100, Boris Brezillon wrote:
> From: Ashley Smith <ashley.smith@collabora.com>
> 
> The timeout logic provided by drm_sched leads to races when we try
> to suspend it while the drm_sched workqueue queues more jobs. Let's
> overhaul the timeout handling in panthor to have our own delayed work
> that's resumed/suspended when a group is resumed/suspended. When an
> actual timeout occurs, we call drm_sched_fault() to report it
> through drm_sched, still. But otherwise, the drm_sched timeout is
> disabled (set to MAX_SCHEDULE_TIMEOUT), which leaves us in control of
> how we protect modifications on the timer.
> 
> One issue seems to be when we call drm_sched_suspend_timeout() from
> both queue_run_job() and tick_work() which could lead to races due to
> drm_sched_suspend_timeout() not having a lock. Another issue seems to
> be in queue_run_job() if the group is not scheduled, we suspend the
> timeout again which undoes what drm_sched_job_begin() did when calling
> drm_sched_start_timeout(). So the timeout does not reset when a job
> is finished.
> 
> 

[…]

> +
> +static void
> +queue_reset_timeout_locked(struct panthor_queue *queue)
> +{
> +	lockdep_assert_held(&queue->fence_ctx.lock);
> +
> +	if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
> +		mod_delayed_work(queue->scheduler.timeout_wq,

Here you are interfering with the scheduler's internals again, don't
you. I think we agreed that we don't want to do such things anymore,
didn't we?

You could write a proper drm_sched API function which serves your
usecase.

Or could maybe DRM_GPU_SCHED_STAT_NO_HANG be returned from your driver
in case an interrupt actually fires?

> +				 &queue->timeout.work,
> +				 msecs_to_jiffies(JOB_TIMEOUT_MS));
> +	}
> +}
> +
> +static bool
> +group_can_run(struct panthor_group *group)
> +{
> +	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
> +	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
> +	       !group->destroyed && group->fatal_queues == 0 &&
> +	       !group->timedout;
> +}
> +
> 

[…]

> +queue_suspend_timeout(struct panthor_queue *queue)
> +{
> +	spin_lock(&queue->fence_ctx.lock);
> +	queue_suspend_timeout_locked(queue);
> +	spin_unlock(&queue->fence_ctx.lock);
> +}
> +
> +static void
> +queue_resume_timeout(struct panthor_queue *queue)
> +{
> +	spin_lock(&queue->fence_ctx.lock);
> +
> +	if (queue_timeout_is_suspended(queue)) {
> +		mod_delayed_work(queue->scheduler.timeout_wq,

There is drm_sched_resume_timeout(). Why can it not be used?

> +				 &queue->timeout.work,
> +				 queue->timeout.remaining);
> +
> +		queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
> +	}
> +
> +	spin_unlock(&queue->fence_ctx.lock);
> +}
> +
> 

[…]

>  
> @@ -3270,6 +3379,11 @@ queue_timedout_job(struct drm_sched_job *sched_job)
>  
>  	queue_start(queue);
>  
> +	/* We already flagged the queue as faulty, make sure we don't get
> +	 * called again.
> +	 */
> +	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> +
>  	return DRM_GPU_SCHED_STAT_RESET;

DRM_GPU_SCHED_STAT_NO_HANG instead of just modifying the scheduler's
internal data??


P.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 12:38   ` Philipp Stanner
@ 2025-11-12 13:31     ` Boris Brezillon
  2025-11-12 13:48       ` Philipp Stanner
  0 siblings, 1 reply; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 13:31 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

Hi Philipp,

On Wed, 12 Nov 2025 13:38:00 +0100
Philipp Stanner <pstanner@redhat.com> wrote:

> On Wed, 2025-11-12 at 13:17 +0100, Boris Brezillon wrote:
> > From: Ashley Smith <ashley.smith@collabora.com>
> > 
> > The timeout logic provided by drm_sched leads to races when we try
> > to suspend it while the drm_sched workqueue queues more jobs. Let's
> > overhaul the timeout handling in panthor to have our own delayed work
> > that's resumed/suspended when a group is resumed/suspended. When an
> > actual timeout occurs, we call drm_sched_fault() to report it
> > through drm_sched, still. But otherwise, the drm_sched timeout is
> > disabled (set to MAX_SCHEDULE_TIMEOUT), which leaves us in control of
> > how we protect modifications on the timer.
> > 
> > One issue seems to be when we call drm_sched_suspend_timeout() from
> > both queue_run_job() and tick_work() which could lead to races due to
> > drm_sched_suspend_timeout() not having a lock. Another issue seems to
> > be in queue_run_job() if the group is not scheduled, we suspend the
> > timeout again which undoes what drm_sched_job_begin() did when calling
> > drm_sched_start_timeout(). So the timeout does not reset when a job
> > is finished.
> > 
> >   
> 
> […]
> 
> > +
> > +static void
> > +queue_reset_timeout_locked(struct panthor_queue *queue)
> > +{
> > +	lockdep_assert_held(&queue->fence_ctx.lock);
> > +
> > +	if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
> > +		mod_delayed_work(queue->scheduler.timeout_wq,  
> 
> Here you are interfering with the scheduler's internals again, don't
> you. I think we agreed that we don't want to do such things anymore,
> didn't we?

We're not really touching drm_gpu_scheduler's internals, we're just
retrieving the timeout workqueue we passed at init time:
panthor_queue::timeout is panthor internals not drm_sched internals.

This being said, I agree we should use ptdev->reset.wq instead of
retrieving the timeout workqueue through the drm_gpu_scheduler object.

Just to be clear, the goal of this patch is to bypass the
drm_gpu_scheduler timeout logic entirely, so we can have our own thing
that's not racy (see below).

> 
> You could write a proper drm_sched API function which serves your
> usecase.

It's not really lack of support for our usecase that drives this
change, but more the fact the current helpers are racy for drivers that
have a 1:1 entity:sched relationship with queues that can be scheduled
out behind drm_gpu_scheduler's back.

> 
> Or could maybe DRM_GPU_SCHED_STAT_NO_HANG be returned from your driver
> in case an interrupt actually fires?

I don't think it helps, see below.

> 
> > +				 &queue->timeout.work,
> > +				 msecs_to_jiffies(JOB_TIMEOUT_MS));
> > +	}
> > +}
> > +
> > +static bool
> > +group_can_run(struct panthor_group *group)
> > +{
> > +	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
> > +	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
> > +	       !group->destroyed && group->fatal_queues == 0 &&
> > +	       !group->timedout;
> > +}
> > +
> >   
> 
> […]
> 
> > +queue_suspend_timeout(struct panthor_queue *queue)
> > +{
> > +	spin_lock(&queue->fence_ctx.lock);
> > +	queue_suspend_timeout_locked(queue);
> > +	spin_unlock(&queue->fence_ctx.lock);
> > +}
> > +
> > +static void
> > +queue_resume_timeout(struct panthor_queue *queue)
> > +{
> > +	spin_lock(&queue->fence_ctx.lock);
> > +
> > +	if (queue_timeout_is_suspended(queue)) {
> > +		mod_delayed_work(queue->scheduler.timeout_wq,  
> 
> There is drm_sched_resume_timeout(). Why can it not be used?

Because it's racy. I don't remember all the details, but IIRC, it had
to do with job insertion calling drm_sched_start_timeout() while we're
calling drm_sched_resume_timeout() from cs_slot_reset_locked(). We
tried to make it work, but we gave up at some point and went for a
driver-specific timer, because ultimately what we want is a per-queue
timeout that we can pause/resume without drm_sched interfering when new
jobs are queued to our ring buffers: we resume when the execution
context (AKA group) is scheduled in, and we pause when this execution
context is scheduled out.

That's the very reason we set drm_gpu_scheduler::timeout to
MAX_SCHEDULE_TIMEOUT at init time (AKA, timeout disabled) and never
touch that again. When our driver-internal timer expires, we forward
the information to the drm_sched layer by calling drm_sched_fault().

> 
> > +				 &queue->timeout.work,
> > +				 queue->timeout.remaining);
> > +
> > +		queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
> > +	}
> > +
> > +	spin_unlock(&queue->fence_ctx.lock);
> > +}
> > +
> >   
> 
> […]
> 
> >  
> > @@ -3270,6 +3379,11 @@ queue_timedout_job(struct drm_sched_job *sched_job)
> >  
> >  	queue_start(queue);
> >  
> > +	/* We already flagged the queue as faulty, make sure we don't get
> > +	 * called again.
> > +	 */
> > +	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> > +
> >  	return DRM_GPU_SCHED_STAT_RESET;  
> 
> DRM_GPU_SCHED_STAT_NO_HANG instead of just modifying the scheduler's
> internal data??


	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;

is a leftover from a previous version. We shouldn't have to modify that
here because the timeout is initialized to MAX_SCHEDULE_TIMEOUT and
never touched again.

Regards,

Boris

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 13:31     ` Boris Brezillon
@ 2025-11-12 13:48       ` Philipp Stanner
  2025-11-12 14:12         ` Boris Brezillon
  0 siblings, 1 reply; 10+ messages in thread
From: Philipp Stanner @ 2025-11-12 13:48 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

On Wed, 2025-11-12 at 14:31 +0100, Boris Brezillon wrote:
> Hi Philipp,
> 
> On Wed, 12 Nov 2025 13:38:00 +0100
> Philipp Stanner <pstanner@redhat.com> wrote:
> 
> > On Wed, 2025-11-12 at 13:17 +0100, Boris Brezillon wrote:
> > > From: Ashley Smith <ashley.smith@collabora.com>
> > > 
> > > The timeout logic provided by drm_sched leads to races when we try
> > > to suspend it while the drm_sched workqueue queues more jobs. Let's
> > > overhaul the timeout handling in panthor to have our own delayed work
> > > that's resumed/suspended when a group is resumed/suspended. When an
> > > actual timeout occurs, we call drm_sched_fault() to report it
> > > through drm_sched, still. But otherwise, the drm_sched timeout is
> > > disabled (set to MAX_SCHEDULE_TIMEOUT), which leaves us in control of
> > > how we protect modifications on the timer.
> > > 
> > > One issue seems to be when we call drm_sched_suspend_timeout() from
> > > both queue_run_job() and tick_work() which could lead to races due to
> > > drm_sched_suspend_timeout() not having a lock. Another issue seems to
> > > be in queue_run_job() if the group is not scheduled, we suspend the
> > > timeout again which undoes what drm_sched_job_begin() did when calling
> > > drm_sched_start_timeout(). So the timeout does not reset when a job
> > > is finished.
> > > 
> > >   
> > 
> > […]
> > 
> > > +
> > > +static void
> > > +queue_reset_timeout_locked(struct panthor_queue *queue)
> > > +{
> > > +	lockdep_assert_held(&queue->fence_ctx.lock);
> > > +
> > > +	if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
> > > +		mod_delayed_work(queue->scheduler.timeout_wq,  
> > 
> > Here you are interfering with the scheduler's internals again, don't
> > you. I think we agreed that we don't want to do such things anymore,
> > didn't we?
> 
> We're not really touching drm_gpu_scheduler's internals, we're just
> retrieving the timeout workqueue we passed at init time:
> panthor_queue::timeout is panthor internals not drm_sched internals.
> 
> This being said, I agree we should use ptdev->reset.wq instead of
> retrieving the timeout workqueue through the drm_gpu_scheduler object.
> 
> Just to be clear, the goal of this patch is to bypass the
> drm_gpu_scheduler timeout logic entirely, so we can have our own thing
> that's not racy (see below).

OK. timeout_wq sharing is intended and allowed, so if that's what
you're doing, good. But I agree that accessing the wq through the
driver's struct is then cleaner and more obviously correct.

> 
> > 
> > You could write a proper drm_sched API function which serves your
> > usecase.
> 
> It's not really lack of support for our usecase that drives this
> change, but more the fact the current helpers are racy for drivers that
> have a 1:1 entity:sched relationship with queues that can be scheduled
> out behind drm_gpu_scheduler's back.

And you also can't stop drm_sched to prevent races?

> 
> > 
> > Or could maybe DRM_GPU_SCHED_STAT_NO_HANG be returned from your driver
> > in case an interrupt actually fires?
> 
> I don't think it helps, see below.
> 
> > 
> > > +				 &queue->timeout.work,
> > > +				 msecs_to_jiffies(JOB_TIMEOUT_MS));
> > > +	}
> > > +}
> > > +
> > > +static bool
> > > +group_can_run(struct panthor_group *group)
> > > +{
> > > +	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
> > > +	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
> > > +	       !group->destroyed && group->fatal_queues == 0 &&
> > > +	       !group->timedout;
> > > +}
> > > +
> > >   
> > 
> > […]
> > 
> > > +queue_suspend_timeout(struct panthor_queue *queue)
> > > +{
> > > +	spin_lock(&queue->fence_ctx.lock);
> > > +	queue_suspend_timeout_locked(queue);
> > > +	spin_unlock(&queue->fence_ctx.lock);
> > > +}
> > > +
> > > +static void
> > > +queue_resume_timeout(struct panthor_queue *queue)
> > > +{
> > > +	spin_lock(&queue->fence_ctx.lock);
> > > +
> > > +	if (queue_timeout_is_suspended(queue)) {
> > > +		mod_delayed_work(queue->scheduler.timeout_wq,  
> > 
> > There is drm_sched_resume_timeout(). Why can it not be used?
> 
> Because it's racy. I don't remember all the details, but IIRC, it had
> to do with job insertion calling drm_sched_start_timeout() while we're
> calling drm_sched_resume_timeout() from cs_slot_reset_locked(). We
> tried to make it work, but we gave up at some point and went for a
> driver-specific timer, because ultimately what we want is a per-queue
> timeout that we can pause/resume without drm_sched interfering when new
> jobs are queued to our ring buffers: we resume when the execution
> context (AKA group) is scheduled in, and we pause when this execution
> context is scheduled out.
> 
> That's the very reason we set drm_gpu_scheduler::timeout to
> MAX_SCHEDULE_TIMEOUT at init time (AKA, timeout disabled) and never
> touch that again. When our driver-internal timer expires, we forward
> the information to the drm_sched layer by calling drm_sched_fault().

That sounds all.. stressful ;)

As you know I only learned a few weeks ago about your group scheduler
on top of drm_sched. I wish I had heard about it when it was
implemented; we might have come up with the idea for drm_jobqueue
sooner.

> 
> > 
> > > +				 &queue->timeout.work,
> > > +				 queue->timeout.remaining);
> > > +
> > > +		queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
> > > +	}
> > > +
> > > +	spin_unlock(&queue->fence_ctx.lock);
> > > +}
> > > +
> > >   
> > 
> > […]
> > 
> > >  
> > > @@ -3270,6 +3379,11 @@ queue_timedout_job(struct drm_sched_job *sched_job)
> > >  
> > >  	queue_start(queue);
> > >  
> > > +	/* We already flagged the queue as faulty, make sure we don't get
> > > +	 * called again.
> > > +	 */
> > > +	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> > > +
> > >  	return DRM_GPU_SCHED_STAT_RESET;  
> > 
> > DRM_GPU_SCHED_STAT_NO_HANG instead of just modifying the scheduler's
> > internal data??
> 
> 
> 	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> 
> is a leftover from a previous version. We shouldn't have to modify that
> here because the timeout is initialized to MAX_SCHEDULE_TIMEOUT and
> never touched again.

So you agree that it can be removed in v8?


P.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 13:48       ` Philipp Stanner
@ 2025-11-12 14:12         ` Boris Brezillon
  2025-11-12 14:23           ` Boris Brezillon
  2025-11-13 10:56           ` Philipp Stanner
  0 siblings, 2 replies; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 14:12 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

On Wed, 12 Nov 2025 14:48:51 +0100
Philipp Stanner <pstanner@redhat.com> wrote:

> On Wed, 2025-11-12 at 14:31 +0100, Boris Brezillon wrote:
> > Hi Philipp,
> > 
> > On Wed, 12 Nov 2025 13:38:00 +0100
> > Philipp Stanner <pstanner@redhat.com> wrote:
> >   
> > > On Wed, 2025-11-12 at 13:17 +0100, Boris Brezillon wrote:  
> > > > From: Ashley Smith <ashley.smith@collabora.com>
> > > > 
> > > > The timeout logic provided by drm_sched leads to races when we try
> > > > to suspend it while the drm_sched workqueue queues more jobs. Let's
> > > > overhaul the timeout handling in panthor to have our own delayed work
> > > > that's resumed/suspended when a group is resumed/suspended. When an
> > > > actual timeout occurs, we call drm_sched_fault() to report it
> > > > through drm_sched, still. But otherwise, the drm_sched timeout is
> > > > disabled (set to MAX_SCHEDULE_TIMEOUT), which leaves us in control of
> > > > how we protect modifications on the timer.
> > > > 
> > > > One issue seems to be when we call drm_sched_suspend_timeout() from
> > > > both queue_run_job() and tick_work() which could lead to races due to
> > > > drm_sched_suspend_timeout() not having a lock. Another issue seems to
> > > > be in queue_run_job() if the group is not scheduled, we suspend the
> > > > timeout again which undoes what drm_sched_job_begin() did when calling
> > > > drm_sched_start_timeout(). So the timeout does not reset when a job
> > > > is finished.
> > > > 
> > > >     
> > > 
> > > […]
> > >   
> > > > +
> > > > +static void
> > > > +queue_reset_timeout_locked(struct panthor_queue *queue)
> > > > +{
> > > > +	lockdep_assert_held(&queue->fence_ctx.lock);
> > > > +
> > > > +	if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
> > > > +		mod_delayed_work(queue->scheduler.timeout_wq,    
> > > 
> > > Here you are interfering with the scheduler's internals again, don't
> > > you. I think we agreed that we don't want to do such things anymore,
> > > didn't we?  
> > 
> > We're not really touching drm_gpu_scheduler's internals, we're just
> > retrieving the timeout workqueue we passed at init time:
> > panthor_queue::timeout is panthor internals not drm_sched internals.
> > 
> > This being said, I agree we should use ptdev->reset.wq instead of
> > retrieving the timeout workqueue through the drm_gpu_scheduler object.
> > 
> > Just to be clear, the goal of this patch is to bypass the
> > drm_gpu_scheduler timeout logic entirely, so we can have our own thing
> > that's not racy (see below).  
> 
> OK. timeout_wq sharing is intended and allowed, so if that's what
> you're doing, good. But I agree that accessing the wq through the
> driver's struct is then cleaner and more obviously correct.

Will do.

> 
> >   
> > > 
> > > You could write a proper drm_sched API function which serves your
> > > usecase.  
> > 
> > It's not really lack of support for our usecase that drives this
> > change, but more the fact the current helpers are racy for drivers that
> > have a 1:1 entity:sched relationship with queues that can be scheduled
> > out behind drm_gpu_scheduler's back.  
> 
> And you also can't stop drm_sched to prevent races?

That's the thing, I don't want to stop the drm_gpu_scheduler attached
to a panthor_queue, I want new jobs to be queued to the ring buffer
until this ring buffer is full (which is controller with the
::credit_limit property), even if the group this queue belongs to is
not currently active on the FW side. Those jobs will get executed at
some later point when the group gets picked by the panthor scheduler.

> 
> >   
> > > 
> > > Or could maybe DRM_GPU_SCHED_STAT_NO_HANG be returned from your driver
> > > in case an interrupt actually fires?  
> > 
> > I don't think it helps, see below.
> >   
> > >   
> > > > +				 &queue->timeout.work,
> > > > +				 msecs_to_jiffies(JOB_TIMEOUT_MS));
> > > > +	}
> > > > +}
> > > > +
> > > > +static bool
> > > > +group_can_run(struct panthor_group *group)
> > > > +{
> > > > +	return group->state != PANTHOR_CS_GROUP_TERMINATED &&
> > > > +	       group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
> > > > +	       !group->destroyed && group->fatal_queues == 0 &&
> > > > +	       !group->timedout;
> > > > +}
> > > > +
> > > >     
> > > 
> > > […]
> > >   
> > > > +queue_suspend_timeout(struct panthor_queue *queue)
> > > > +{
> > > > +	spin_lock(&queue->fence_ctx.lock);
> > > > +	queue_suspend_timeout_locked(queue);
> > > > +	spin_unlock(&queue->fence_ctx.lock);
> > > > +}
> > > > +
> > > > +static void
> > > > +queue_resume_timeout(struct panthor_queue *queue)
> > > > +{
> > > > +	spin_lock(&queue->fence_ctx.lock);
> > > > +
> > > > +	if (queue_timeout_is_suspended(queue)) {
> > > > +		mod_delayed_work(queue->scheduler.timeout_wq,    
> > > 
> > > There is drm_sched_resume_timeout(). Why can it not be used?  
> > 
> > Because it's racy. I don't remember all the details, but IIRC, it had
> > to do with job insertion calling drm_sched_start_timeout() while we're
> > calling drm_sched_resume_timeout() from cs_slot_reset_locked(). We
> > tried to make it work, but we gave up at some point and went for a
> > driver-specific timer, because ultimately what we want is a per-queue
> > timeout that we can pause/resume without drm_sched interfering when new
> > jobs are queued to our ring buffers: we resume when the execution
> > context (AKA group) is scheduled in, and we pause when this execution
> > context is scheduled out.
> > 
> > That's the very reason we set drm_gpu_scheduler::timeout to
> > MAX_SCHEDULE_TIMEOUT at init time (AKA, timeout disabled) and never
> > touch that again. When our driver-internal timer expires, we forward
> > the information to the drm_sched layer by calling drm_sched_fault().  
> 
> That sounds all.. stressful ;)

Yeah, it's not ideal :-/.

> 
> As you know I only learned a few weeks ago about your group scheduler
> on top of drm_sched. I wish I had heard about it when it was
> implemented; we might have come up with the idea for drm_jobqueue
> sooner.

Might have simplified things, I guess, but that's life, and I'm happy
to transition to drm_jobqueue when it's deemed ready.

> 
> >   
> > >   
> > > > +				 &queue->timeout.work,
> > > > +				 queue->timeout.remaining);
> > > > +
> > > > +		queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
> > > > +	}
> > > > +
> > > > +	spin_unlock(&queue->fence_ctx.lock);
> > > > +}
> > > > +
> > > >     
> > > 
> > > […]
> > >   
> > > >  
> > > > @@ -3270,6 +3379,11 @@ queue_timedout_job(struct drm_sched_job *sched_job)
> > > >  
> > > >  	queue_start(queue);
> > > >  
> > > > +	/* We already flagged the queue as faulty, make sure we don't get
> > > > +	 * called again.
> > > > +	 */
> > > > +	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> > > > +
> > > >  	return DRM_GPU_SCHED_STAT_RESET;    
> > > 
> > > DRM_GPU_SCHED_STAT_NO_HANG instead of just modifying the scheduler's
> > > internal data??  
> > 
> > 
> > 	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> > 
> > is a leftover from a previous version. We shouldn't have to modify that
> > here because the timeout is initialized to MAX_SCHEDULE_TIMEOUT and
> > never touched again.  
> 
> So you agree that it can be removed in v8?

Yep, I think it can go away. I'll make sure it still works without it,
and also get the wq from some driver fields instead of going back to
drm_gpu_scheduler::timeout_wq.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 14:12         ` Boris Brezillon
@ 2025-11-12 14:23           ` Boris Brezillon
  2025-11-13 10:56           ` Philipp Stanner
  1 sibling, 0 replies; 10+ messages in thread
From: Boris Brezillon @ 2025-11-12 14:23 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

On Wed, 12 Nov 2025 15:12:53 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> > OK. timeout_wq sharing is intended and allowed, so if that's what
> > you're doing, good. But I agree that accessing the wq through the
> > driver's struct is then cleaner and more obviously correct.  
> 
> Will do.

Actually, after looking into that, it seems I'd have to go back to
dev_get_drvdata(drm_gpu_scheduler::dev)::reset_wq to get the timeout
workqueue, so I think I'd prefer to keep using
drm_gpu_scheduler::timeout_wq unless that's really seen as a layering
violation (but I'd argue that accessing drm_gpu_scheduler::dev looks
like the same kind of layering violation to me).

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-12 14:12         ` Boris Brezillon
  2025-11-12 14:23           ` Boris Brezillon
@ 2025-11-13 10:56           ` Philipp Stanner
  2025-11-13 11:05             ` Boris Brezillon
  1 sibling, 1 reply; 10+ messages in thread
From: Philipp Stanner @ 2025-11-13 10:56 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

On Wed, 2025-11-12 at 15:12 +0100, Boris Brezillon wrote:
> On Wed, 12 Nov 2025 14:48:51 +0100
> Philipp Stanner <pstanner@redhat.com> wrote:
> 
> > 

[…]

> > >   
> > > > 
> > > > You could write a proper drm_sched API function which serves your
> > > > usecase.  
> > > 
> > > It's not really lack of support for our usecase that drives this
> > > change, but more the fact the current helpers are racy for drivers that
> > > have a 1:1 entity:sched relationship with queues that can be scheduled
> > > out behind drm_gpu_scheduler's back.  
> > 
> > And you also can't stop drm_sched to prevent races?
> 
> That's the thing, I don't want to stop the drm_gpu_scheduler attached
> to a panthor_queue, I want new jobs to be queued to the ring buffer
> until this ring buffer is full (which is controller with the
> ::credit_limit property), even if the group this queue belongs to is
> not currently active on the FW side. Those jobs will get executed at
> some later point when the group gets picked by the panthor scheduler.

Ah, OK! Understood.

> > 
> > As you know I only learned a few weeks ago about your group scheduler
> > on top of drm_sched. I wish I had heard about it when it was
> > implemented; we might have come up with the idea for drm_jobqueue
> > sooner.
> 
> Might have simplified things, I guess, but that's life, and I'm happy
> to transition to drm_jobqueue when it's deemed ready.

JQ is in Rust, potentially one day with a C ABI. So that could only
happen if your driver's users are OK with relying on LLVM for building
the kernel.

BTW would be interesting for me to know to what degree that's a problem
for common distributions and users.

> 
> > 
> > >   
> > > > 

[…]

> > > 
> > > 
> > > 	queue->scheduler.timeout = MAX_SCHEDULE_TIMEOUT;
> > > 
> > > is a leftover from a previous version. We shouldn't have to modify that
> > > here because the timeout is initialized to MAX_SCHEDULE_TIMEOUT and
> > > never touched again.  
> > 
> > So you agree that it can be removed in v8?
> 
> Yep, I think it can go away. I'll make sure it still works without it,
> and also get the wq from some driver fields instead of going back to
> drm_gpu_scheduler::timeout_wq.
> 

I saw your other reply.

I mean, it's all not ideal, but as already stated timeout_wq is
intended for sharing and, furthermore, we have so many users of interna
by now that it often can't even be determined anymore what's legal and
what's not. Let's live with it for now.


P.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job
  2025-11-13 10:56           ` Philipp Stanner
@ 2025-11-13 11:05             ` Boris Brezillon
  0 siblings, 0 replies; 10+ messages in thread
From: Boris Brezillon @ 2025-11-13 11:05 UTC (permalink / raw)
  To: Philipp Stanner
  Cc: Steven Price, Liviu Dudau, Adrián Larumbe, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, dri-devel, Detlev Casanova,
	Ashley Smith, kernel

On Thu, 13 Nov 2025 11:56:03 +0100
Philipp Stanner <pstanner@redhat.com> wrote:

> On Wed, 2025-11-12 at 15:12 +0100, Boris Brezillon wrote:
> > On Wed, 12 Nov 2025 14:48:51 +0100
> > Philipp Stanner <pstanner@redhat.com> wrote:
> >   
> > >   
> 
> […]
> 
> > > >     
> > > > > 
> > > > > You could write a proper drm_sched API function which serves your
> > > > > usecase.    
> > > > 
> > > > It's not really lack of support for our usecase that drives this
> > > > change, but more the fact the current helpers are racy for drivers that
> > > > have a 1:1 entity:sched relationship with queues that can be scheduled
> > > > out behind drm_gpu_scheduler's back.    
> > > 
> > > And you also can't stop drm_sched to prevent races?  
> > 
> > That's the thing, I don't want to stop the drm_gpu_scheduler attached
> > to a panthor_queue, I want new jobs to be queued to the ring buffer
> > until this ring buffer is full (which is controller with the
> > ::credit_limit property), even if the group this queue belongs to is
> > not currently active on the FW side. Those jobs will get executed at
> > some later point when the group gets picked by the panthor scheduler.  
> 
> Ah, OK! Understood.
> 
> > > 
> > > As you know I only learned a few weeks ago about your group scheduler
> > > on top of drm_sched. I wish I had heard about it when it was
> > > implemented; we might have come up with the idea for drm_jobqueue
> > > sooner.  
> > 
> > Might have simplified things, I guess, but that's life, and I'm happy
> > to transition to drm_jobqueue when it's deemed ready.  
> 
> JQ is in Rust, potentially one day with a C ABI. So that could only
> happen if your driver's users are OK with relying on LLVM for building
> the kernel.
> 
> BTW would be interesting for me to know to what degree that's a problem
> for common distributions and users.

That's a good question. I must admit I never thought of this because I
usually build my kernels on a full-blown distro with both llvm and gcc
installed, so I can easily pick the one I need for the task. For distro
builds that should be okay, but for {yocto,buildroot,custom}-based
build systems the transition might be painful/annoying, but I'd expect
people to move to llvm if they have to. I mean, the ultimate goal is
for Tyr to replace Panthor so we don't have to maintain two drivers, so
if that's going to be a problem, I'd rather know it now :-).

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-11-13 11:05 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-12 12:17 [PATCH v7 0/2] drm/panthor: Scheduler fixes for termination failure and timeouts Boris Brezillon
2025-11-12 12:17 ` [PATCH v7 1/2] drm/panthor: Make the timeout per-queue instead of per-job Boris Brezillon
2025-11-12 12:38   ` Philipp Stanner
2025-11-12 13:31     ` Boris Brezillon
2025-11-12 13:48       ` Philipp Stanner
2025-11-12 14:12         ` Boris Brezillon
2025-11-12 14:23           ` Boris Brezillon
2025-11-13 10:56           ` Philipp Stanner
2025-11-13 11:05             ` Boris Brezillon
2025-11-12 12:17 ` [PATCH v7 2/2] drm/panthor: Reset queue slots if termination fails Boris Brezillon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.