* [PATCH 0/2] drm/sched: Fair policy fixups @ 2026-08-14 7:57 Tvrtko Ursulin 2026-08-14 7:57 ` [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime Tvrtko Ursulin 2026-08-14 7:57 ` [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime Tvrtko Ursulin 0 siblings, 2 replies; 8+ messages in thread From: Tvrtko Ursulin @ 2026-08-14 7:57 UTC (permalink / raw) To: amd-gfx, dri-devel Cc: kernel-dev, Tvrtko Ursulin, Christian König, Danilo Krummrich, Luke.Wildhardt, Matthew Brost, Philipp Stanner, Pierre-Eric Pelloux-Prayer, Vitaly Prosyak Two fixes to address the two issues found in: https://lore.kernel.org/dri-devel/TfhgV1W0W5LI6RWUO6J35B3R8QIYH_FN3Eihzdo_9PH39hfn1AVByT-QBPHmWiAR-L0Kqi2sppM_EhFPKwZPGmb5pFgpF2MrzeFzkZDmpG8=@proton.me/ Many thanks to Luke for reporting, sharing one AI proposed fix, and the super quick testing turnaround! After these two we can hopefully work at promoting the "fair" policy back to the default in an upcoming kernel release, followed by removing the FIFO and round-robin, and then re-applying the code base simplifications which we had to temporarily revert. Cc: Christian König <christian.koenig@amd.com> Cc: Danilo Krummrich <dakr@kernel.org> Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost <matthew.brost@intel.com> Cc: Philipp Stanner <phasta@kernel.org> Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> Tvrtko Ursulin (2): drm/sched: Do not restore unsaved virtual runtime drm/sched: Ensure monotonic min_vruntime drivers/gpu/drm/scheduler/sched_entity.c | 8 ++- drivers/gpu/drm/scheduler/sched_rq.c | 85 ++++++++---------------- include/drm/gpu_scheduler.h | 2 + 3 files changed, 37 insertions(+), 58 deletions(-) -- 2.54.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime 2026-08-14 7:57 [PATCH 0/2] drm/sched: Fair policy fixups Tvrtko Ursulin @ 2026-08-14 7:57 ` Tvrtko Ursulin 2026-08-14 8:10 ` sashiko-bot 2026-08-14 8:53 ` Matthew Brost 2026-08-14 7:57 ` [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime Tvrtko Ursulin 1 sibling, 2 replies; 8+ messages in thread From: Tvrtko Ursulin @ 2026-08-14 7:57 UTC (permalink / raw) To: amd-gfx, dri-devel Cc: kernel-dev, Tvrtko Ursulin, Luke.Wildhardt, Christian König, Danilo Krummrich, Philipp Stanner, Pierre-Eric Pelloux-Prayer, Matthew Brost, Vitaly Prosyak Prevent pushing a new job to an entity seeing it being the first in the queue, and hence entering the drm_sched_rq_add_entity() path, if the pop side in drm_sched_entity_pop_job() has de-queued the job but not yet updated the saved virtual time. We do this by pulling the locked sections out to encompass both the queue push/pop and corresponding rbtree management. Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") Suggested-by: Luke.Wildhardt@proton.me # via Claude Opus Tested-by: Luke.Wildhardt@proton.me Cc: Christian König <christian.koenig@amd.com> Cc: Danilo Krummrich <dakr@kernel.org> Cc: Philipp Stanner <phasta@kernel.org> Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Cc: Matthew Brost <matthew.brost@intel.com> Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> --- drivers/gpu/drm/scheduler/sched_entity.c | 8 +++++++- drivers/gpu/drm/scheduler/sched_rq.c | 20 +++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index a4a7efdbf229..673ca9cbf362 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -559,9 +559,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) */ smp_wmb(); + spin_lock(&entity->lock); spsc_queue_pop(&entity->job_queue); - drm_sched_rq_pop_entity(entity); + spin_unlock(&entity->lock); /* Jobs and entities might have different lifecycles. Since we're * removing the job from the entities queue, set the jobs entity pointer @@ -647,6 +648,9 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) * Make sure to set the submit_ts first, to avoid a race. */ sched_job->submit_ts = submit_ts = ktime_get(); + + spin_lock(&entity->lock); + first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); /* first job wakes up scheduler */ @@ -657,5 +661,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) if (sched) drm_sched_wakeup(sched); } + + spin_unlock(&entity->lock); } EXPORT_SYMBOL(drm_sched_entity_push_job); diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c index 0464d324d98d..23f46ec610e7 100644 --- a/drivers/gpu/drm/scheduler/sched_rq.c +++ b/drivers/gpu/drm/scheduler/sched_rq.c @@ -257,19 +257,17 @@ static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity) struct drm_gpu_scheduler * drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) { + struct drm_sched_rq *rq = entity->rq; struct drm_gpu_scheduler *sched; - struct drm_sched_rq *rq; /* Add the entity to the run queue */ - spin_lock(&entity->lock); + lockdep_assert_held(&entity->lock); + if (entity->stopped) { - spin_unlock(&entity->lock); - DRM_ERROR("Trying to push to a killed entity\n"); return NULL; } - rq = entity->rq; spin_lock(&rq->lock); sched = rq->sched; @@ -289,7 +287,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) drm_sched_rq_update_fifo_locked(entity, rq, ts); spin_unlock(&rq->lock); - spin_unlock(&entity->lock); return sched; } @@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq, */ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) { + struct drm_sched_rq *rq = entity->rq; struct drm_sched_job *next_job; - struct drm_sched_rq *rq; + + lockdep_assert_held(&entity->lock); + + spin_lock(&rq->lock); /* * Update the entity's location in the min heap according to * the timestamp of the next job, if any. */ - spin_lock(&entity->lock); - rq = entity->rq; - spin_lock(&rq->lock); next_job = drm_sched_entity_queue_peek(entity); if (next_job) { ktime_t ts; @@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) drm_sched_entity_save_vruntime(entity, min_vruntime); } } + spin_unlock(&rq->lock); - spin_unlock(&entity->lock); } /** -- 2.54.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime 2026-08-14 7:57 ` [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime Tvrtko Ursulin @ 2026-08-14 8:10 ` sashiko-bot 2026-08-14 8:53 ` Matthew Brost 1 sibling, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-08-14 8:10 UTC (permalink / raw) To: Tvrtko Ursulin; +Cc: dri-devel Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] A killed and stopped scheduler entity can be unconditionally added back to the runqueue's rbtree, leading to a use-after-free when the entity is subsequently freed by the driver. -- --- Patch [1]: [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime --- commit 8feac77c9420bd8a6d250c6a988250fe755b6870 Author: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> Subject: drm/sched: Do not restore unsaved virtual runtime This patch modifies the queue push/pop and runqueue management paths in the drm scheduler. It extends the scope of entity->lock to encompass both queue management and rbtree updates. This prevents a race condition where a new job could be pushed to an entity before its virtual runtime was correctly saved. > diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c > index 0464d324d98d5..23f46ec610e78 100644 > --- a/drivers/gpu/drm/scheduler/sched_rq.c > +++ b/drivers/gpu/drm/scheduler/sched_rq.c [ ... ] > @@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq, > */ > void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) > { > + struct drm_sched_rq *rq = entity->rq; > struct drm_sched_job *next_job; > - struct drm_sched_rq *rq; > + > + lockdep_assert_held(&entity->lock); > + > + spin_lock(&rq->lock); > > /* > * Update the entity's location in the min heap according to > * the timestamp of the next job, if any. > */ > - spin_lock(&entity->lock); > - rq = entity->rq; > - spin_lock(&rq->lock); > next_job = drm_sched_entity_queue_peek(entity); > if (next_job) { [Severity: High] This is a pre-existing issue, but can this lead to a use-after-free if the entity is killed concurrently? If drm_sched_entity_kill() in drivers/gpu/drm/scheduler/sched_entity.c is called on an entity with multiple pending jobs concurrently with the scheduler thread popping a job: drm_sched_entity_kill() { ... spin_lock(&entity->lock); entity->stopped = true; drm_sched_rq_remove_entity(entity->rq, entity); spin_unlock(&entity->lock); ... } Meanwhile, the scheduler thread popping a job calls drm_sched_rq_pop_entity() in drivers/gpu/drm/scheduler/sched_rq.c. Since next_job is not NULL, the code takes this path and calls drm_sched_rq_update_fifo_locked(), which unconditionally adds the entity back to the runqueue's rbtree, ignoring entity->stopped. When the killer thread wakes up and finishes, the entity is freed but still linked in the rbtree, which corrupts the rbtree. Should drm_sched_rq_pop_entity() check entity->stopped before re-adding the entity to the runqueue, similar to the check in drm_sched_rq_add_entity()? > ktime_t ts; > @@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) > drm_sched_entity_save_vruntime(entity, min_vruntime); > } > } > + > spin_unlock(&rq->lock); > - spin_unlock(&entity->lock); > } > > /** -- Sashiko AI review · https://sashiko.dev/#/patchset/20260814075739.32481-1-tvrtko.ursulin@igalia.com?part=1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime 2026-08-14 7:57 ` [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime Tvrtko Ursulin 2026-08-14 8:10 ` sashiko-bot @ 2026-08-14 8:53 ` Matthew Brost 2026-08-14 9:58 ` Tvrtko Ursulin 1 sibling, 1 reply; 8+ messages in thread From: Matthew Brost @ 2026-08-14 8:53 UTC (permalink / raw) To: Tvrtko Ursulin Cc: amd-gfx, dri-devel, kernel-dev, Luke.Wildhardt, Christian König, Danilo Krummrich, Philipp Stanner, Pierre-Eric Pelloux-Prayer, Vitaly Prosyak On Fri, Aug 14, 2026 at 08:57:38AM +0100, Tvrtko Ursulin wrote: > Prevent pushing a new job to an entity seeing it being the first in the > queue, and hence entering the drm_sched_rq_add_entity() path, if the pop > side in drm_sched_entity_pop_job() has de-queued the job but not yet > updated the saved virtual time. > > We do this by pulling the locked sections out to encompass both the queue > push/pop and corresponding rbtree management. > > Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> > Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") > Suggested-by: Luke.Wildhardt@proton.me # via Claude Opus > Tested-by: Luke.Wildhardt@proton.me > Cc: Christian König <christian.koenig@amd.com> > Cc: Danilo Krummrich <dakr@kernel.org> > Cc: Philipp Stanner <phasta@kernel.org> > Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> > Cc: Matthew Brost <matthew.brost@intel.com> Ugh. Idiom: "How much longer are we going to keep polishing this turd?" The revert of FAIR IMO shows it basically time for a rewrite, cough - DRM dep. If we can agree, I'll prioritize, if not we'll be here seemingly forever. Matt > Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> > --- > drivers/gpu/drm/scheduler/sched_entity.c | 8 +++++++- > drivers/gpu/drm/scheduler/sched_rq.c | 20 +++++++++----------- > 2 files changed, 16 insertions(+), 12 deletions(-) > > diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c > index a4a7efdbf229..673ca9cbf362 100644 > --- a/drivers/gpu/drm/scheduler/sched_entity.c > +++ b/drivers/gpu/drm/scheduler/sched_entity.c > @@ -559,9 +559,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) > */ > smp_wmb(); > > + spin_lock(&entity->lock); > spsc_queue_pop(&entity->job_queue); > - > drm_sched_rq_pop_entity(entity); > + spin_unlock(&entity->lock); > > /* Jobs and entities might have different lifecycles. Since we're > * removing the job from the entities queue, set the jobs entity pointer > @@ -647,6 +648,9 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) > * Make sure to set the submit_ts first, to avoid a race. > */ > sched_job->submit_ts = submit_ts = ktime_get(); > + > + spin_lock(&entity->lock); > + > first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); > > /* first job wakes up scheduler */ > @@ -657,5 +661,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) > if (sched) > drm_sched_wakeup(sched); > } > + > + spin_unlock(&entity->lock); > } > EXPORT_SYMBOL(drm_sched_entity_push_job); > diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c > index 0464d324d98d..23f46ec610e7 100644 > --- a/drivers/gpu/drm/scheduler/sched_rq.c > +++ b/drivers/gpu/drm/scheduler/sched_rq.c > @@ -257,19 +257,17 @@ static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity) > struct drm_gpu_scheduler * > drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) > { > + struct drm_sched_rq *rq = entity->rq; > struct drm_gpu_scheduler *sched; > - struct drm_sched_rq *rq; > > /* Add the entity to the run queue */ > - spin_lock(&entity->lock); > + lockdep_assert_held(&entity->lock); > + > if (entity->stopped) { > - spin_unlock(&entity->lock); > - > DRM_ERROR("Trying to push to a killed entity\n"); > return NULL; > } > > - rq = entity->rq; > spin_lock(&rq->lock); > sched = rq->sched; > > @@ -289,7 +287,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) > drm_sched_rq_update_fifo_locked(entity, rq, ts); > > spin_unlock(&rq->lock); > - spin_unlock(&entity->lock); > > return sched; > } > @@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq, > */ > void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) > { > + struct drm_sched_rq *rq = entity->rq; > struct drm_sched_job *next_job; > - struct drm_sched_rq *rq; > + > + lockdep_assert_held(&entity->lock); > + > + spin_lock(&rq->lock); > > /* > * Update the entity's location in the min heap according to > * the timestamp of the next job, if any. > */ > - spin_lock(&entity->lock); > - rq = entity->rq; > - spin_lock(&rq->lock); > next_job = drm_sched_entity_queue_peek(entity); > if (next_job) { > ktime_t ts; > @@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) > drm_sched_entity_save_vruntime(entity, min_vruntime); > } > } > + > spin_unlock(&rq->lock); > - spin_unlock(&entity->lock); > } > > /** > -- > 2.54.0 > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime 2026-08-14 8:53 ` Matthew Brost @ 2026-08-14 9:58 ` Tvrtko Ursulin 0 siblings, 0 replies; 8+ messages in thread From: Tvrtko Ursulin @ 2026-08-14 9:58 UTC (permalink / raw) To: Matthew Brost Cc: amd-gfx, dri-devel, kernel-dev, Luke.Wildhardt, Christian König, Danilo Krummrich, Philipp Stanner, Pierre-Eric Pelloux-Prayer, Vitaly Prosyak On 14/08/2026 09:53, Matthew Brost wrote: > On Fri, Aug 14, 2026 at 08:57:38AM +0100, Tvrtko Ursulin wrote: >> Prevent pushing a new job to an entity seeing it being the first in the >> queue, and hence entering the drm_sched_rq_add_entity() path, if the pop >> side in drm_sched_entity_pop_job() has de-queued the job but not yet >> updated the saved virtual time. >> >> We do this by pulling the locked sections out to encompass both the queue >> push/pop and corresponding rbtree management. >> >> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> >> Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") >> Suggested-by: Luke.Wildhardt@proton.me # via Claude Opus >> Tested-by: Luke.Wildhardt@proton.me >> Cc: Christian König <christian.koenig@amd.com> >> Cc: Danilo Krummrich <dakr@kernel.org> >> Cc: Philipp Stanner <phasta@kernel.org> >> Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> >> Cc: Matthew Brost <matthew.brost@intel.com> > > Ugh. Idiom: > > "How much longer are we going to keep polishing this turd?" > > The revert of FAIR IMO shows it basically time for a rewrite, cough - > DRM dep. To give any external readers clarity - hypothetical new DRM dep and fair policy for the current scheduler address separate and distinct kernel drivers. Or to put it differently, the regression which warranted a revert does not apply at all to any of the drivers DRM dep would be the new solution for. Better scheduler for drivers not covered by the DRM dep proposal is still required should DRM dep happen or not. Having clarified that... > If we can agree, I'll prioritize, if not we'll be here seemingly > forever. ...for me that's fine albeit unfortunate. I proposed refactoring the existing scheduler to split it at the backend submission level by sched ops and rq ops, with a future route to split the data structures as well, but that was rejected as a direction. So it seems some flavour of two schedulers will happen, but will it be DRM dep, or jobq in Rust it's up to the (more) interested parties to agree upon. Regards, Tvrtko >> Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> >> --- >> drivers/gpu/drm/scheduler/sched_entity.c | 8 +++++++- >> drivers/gpu/drm/scheduler/sched_rq.c | 20 +++++++++----------- >> 2 files changed, 16 insertions(+), 12 deletions(-) >> >> diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c >> index a4a7efdbf229..673ca9cbf362 100644 >> --- a/drivers/gpu/drm/scheduler/sched_entity.c >> +++ b/drivers/gpu/drm/scheduler/sched_entity.c >> @@ -559,9 +559,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) >> */ >> smp_wmb(); >> >> + spin_lock(&entity->lock); >> spsc_queue_pop(&entity->job_queue); >> - >> drm_sched_rq_pop_entity(entity); >> + spin_unlock(&entity->lock); >> >> /* Jobs and entities might have different lifecycles. Since we're >> * removing the job from the entities queue, set the jobs entity pointer >> @@ -647,6 +648,9 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) >> * Make sure to set the submit_ts first, to avoid a race. >> */ >> sched_job->submit_ts = submit_ts = ktime_get(); >> + >> + spin_lock(&entity->lock); >> + >> first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); >> >> /* first job wakes up scheduler */ >> @@ -657,5 +661,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) >> if (sched) >> drm_sched_wakeup(sched); >> } >> + >> + spin_unlock(&entity->lock); >> } >> EXPORT_SYMBOL(drm_sched_entity_push_job); >> diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c >> index 0464d324d98d..23f46ec610e7 100644 >> --- a/drivers/gpu/drm/scheduler/sched_rq.c >> +++ b/drivers/gpu/drm/scheduler/sched_rq.c >> @@ -257,19 +257,17 @@ static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity) >> struct drm_gpu_scheduler * >> drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) >> { >> + struct drm_sched_rq *rq = entity->rq; >> struct drm_gpu_scheduler *sched; >> - struct drm_sched_rq *rq; >> >> /* Add the entity to the run queue */ >> - spin_lock(&entity->lock); >> + lockdep_assert_held(&entity->lock); >> + >> if (entity->stopped) { >> - spin_unlock(&entity->lock); >> - >> DRM_ERROR("Trying to push to a killed entity\n"); >> return NULL; >> } >> >> - rq = entity->rq; >> spin_lock(&rq->lock); >> sched = rq->sched; >> >> @@ -289,7 +287,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) >> drm_sched_rq_update_fifo_locked(entity, rq, ts); >> >> spin_unlock(&rq->lock); >> - spin_unlock(&entity->lock); >> >> return sched; >> } >> @@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq, >> */ >> void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) >> { >> + struct drm_sched_rq *rq = entity->rq; >> struct drm_sched_job *next_job; >> - struct drm_sched_rq *rq; >> + >> + lockdep_assert_held(&entity->lock); >> + >> + spin_lock(&rq->lock); >> >> /* >> * Update the entity's location in the min heap according to >> * the timestamp of the next job, if any. >> */ >> - spin_lock(&entity->lock); >> - rq = entity->rq; >> - spin_lock(&rq->lock); >> next_job = drm_sched_entity_queue_peek(entity); >> if (next_job) { >> ktime_t ts; >> @@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) >> drm_sched_entity_save_vruntime(entity, min_vruntime); >> } >> } >> + >> spin_unlock(&rq->lock); >> - spin_unlock(&entity->lock); >> } >> >> /** >> -- >> 2.54.0 >> ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime 2026-08-14 7:57 [PATCH 0/2] drm/sched: Fair policy fixups Tvrtko Ursulin 2026-08-14 7:57 ` [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime Tvrtko Ursulin @ 2026-08-14 7:57 ` Tvrtko Ursulin 2026-08-14 8:09 ` sashiko-bot 1 sibling, 1 reply; 8+ messages in thread From: Tvrtko Ursulin @ 2026-08-14 7:57 UTC (permalink / raw) To: amd-gfx, dri-devel Cc: kernel-dev, Tvrtko Ursulin, Luke.Wildhardt, Christian König, Danilo Krummrich, Matthew Brost, Philipp Stanner, Pierre-Eric Pelloux-Prayer, Vitaly Prosyak min_vruntime handling had a bug where if an entity never exited the run- queue it could get penalised by its virtual runtime only ever growing, while the periodically exiting and re-joining entities could repeatedly get pulled ahead of it. Fix it by making the min_vruntime strictly monotonic by tracking it separately (an keeping it always up to date) instead of fetching what happens to be the top of the tree sorted by virtual runtime. While at it replace open coded ktime_t comparison with the correct ktime_after() helper. Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") Tested-by: Luke.Wildhardt@proton.me Cc: Christian König <christian.koenig@amd.com> Cc: Danilo Krummrich <dakr@kernel.org> Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost <matthew.brost@intel.com> Cc: Philipp Stanner <phasta@kernel.org> Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> --- drivers/gpu/drm/scheduler/sched_rq.c | 65 ++++++++-------------------- include/drm/gpu_scheduler.h | 2 + 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c index 23f46ec610e7..bc8363974a27 100644 --- a/drivers/gpu/drm/scheduler/sched_rq.c +++ b/drivers/gpu/drm/scheduler/sched_rq.c @@ -98,6 +98,7 @@ void drm_sched_rq_init(struct drm_gpu_scheduler *sched, rq->rb_tree_root = RB_ROOT_CACHED; rq->sched = sched; rq->head_prio = DRM_SCHED_PRIORITY_INVALID; + rq->min_vruntime = 0; } /* @@ -120,28 +121,6 @@ static const unsigned int vruntime_shift[] = { [DRM_SCHED_PRIORITY_LOW] = 7, }; -static ktime_t -drm_sched_rq_get_min_vruntime(struct drm_sched_rq *rq) -{ - ktime_t vruntime = 0; - struct rb_node *rb; - - lockdep_assert_held(&rq->lock); - - rb = rb_first_cached(&rq->rb_tree_root); - if (rb) { - struct drm_sched_entity *entity = - rb_entry(rb, typeof(*entity), rb_tree_node); - struct drm_sched_entity_stats *stats = entity->stats; - - spin_lock(&stats->lock); - vruntime = stats->vruntime; - spin_unlock(&stats->lock); - } - - return vruntime; -} - static void drm_sched_entity_save_vruntime(struct drm_sched_entity *entity, ktime_t min_vruntime) @@ -151,7 +130,7 @@ drm_sched_entity_save_vruntime(struct drm_sched_entity *entity, spin_lock(&stats->lock); vruntime = stats->vruntime; - if (min_vruntime && vruntime > min_vruntime) + if (ktime_after(vruntime, min_vruntime)) vruntime = ktime_sub(vruntime, min_vruntime); else vruntime = 0; @@ -239,11 +218,6 @@ static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity) return runtime; } -static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity) -{ - return drm_sched_entity_update_vruntime(entity); -} - /** * drm_sched_rq_add_entity - add an entity * @entity: scheduler entity @@ -276,13 +250,11 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) list_add_tail(&entity->list, &rq->entities); } - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { - ts = drm_sched_rq_get_min_vruntime(rq); - ts = drm_sched_entity_restore_vruntime(entity, ts, + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) + ts = drm_sched_entity_restore_vruntime(entity, rq->min_vruntime, rq->head_prio); - } else if (drm_sched_policy == DRM_SCHED_POLICY_RR) { + else if (drm_sched_policy == DRM_SCHED_POLICY_RR) ts = entity->rr_ts; - } drm_sched_rq_update_fifo_locked(entity, rq, ts); @@ -342,6 +314,7 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) { struct drm_sched_rq *rq = entity->rq; struct drm_sched_job *next_job; + ktime_t ts; lockdep_assert_held(&entity->lock); @@ -351,27 +324,27 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) * Update the entity's location in the min heap according to * the timestamp of the next job, if any. */ + + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { + ts = drm_sched_entity_update_vruntime(entity); + if (ktime_after(ts, rq->min_vruntime)) + rq->min_vruntime = ts; + } + next_job = drm_sched_entity_queue_peek(entity); if (next_job) { - ktime_t ts; - - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) - ts = drm_sched_entity_get_job_ts(entity); - else if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) + if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) ts = next_job->submit_ts; - else + else if (drm_sched_policy == DRM_SCHED_POLICY_RR) ts = drm_sched_rq_next_rr_ts(rq, entity); drm_sched_rq_update_fifo_locked(entity, rq, ts); } else { + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) + drm_sched_entity_save_vruntime(entity, + rq->min_vruntime); + drm_sched_rq_remove_fifo_locked(entity, rq); - - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { - ktime_t min_vruntime; - - min_vruntime = drm_sched_rq_get_min_vruntime(rq); - drm_sched_entity_save_vruntime(entity, min_vruntime); - } } spin_unlock(&rq->lock); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 7a64cc11de08..92d1a6ff9ac0 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -267,6 +267,7 @@ struct drm_sched_entity { * @entities: list of the entities to be scheduled. * @rb_tree_root: root of time based priority queue of entities for FIFO scheduling * @head_prio: priority of the top tree element. + * @min_vruntime: Minimum virtual runtime for the run-queue. * * Run queue is a set of entities scheduling command submissions for * one specific ring. It implements the scheduling policy that selects @@ -281,6 +282,7 @@ struct drm_sched_rq { struct list_head entities; struct rb_root_cached rb_tree_root; enum drm_sched_priority head_prio; + ktime_t min_vruntime; }; /** -- 2.54.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime 2026-08-14 7:57 ` [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime Tvrtko Ursulin @ 2026-08-14 8:09 ` sashiko-bot 2026-08-14 10:34 ` Tvrtko Ursulin 0 siblings, 1 reply; 8+ messages in thread From: sashiko-bot @ 2026-08-14 8:09 UTC (permalink / raw) To: Tvrtko Ursulin; +Cc: dri-devel Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] drm_sched_entity_save_vruntime() always saves a virtual runtime lag of 0 due to an impossible condition, permanently destroying the entity's relative lag state. - [Medium] rq->min_vruntime incorrectly tracks the maximum updated vruntime of the executing entity rather than the true minimum of the runqueue, penalizing newly enqueued entities. -- --- Patch [2]: [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime --- commit 840e24cc75ac9a836c633b23cb95dda0e0dc3b75 Author: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> drm/sched: Ensure monotonic min_vruntime min_vruntime handling had a bug where if an entity never exited the run- queue it could get penalised by its virtual runtime only ever growing, while the periodically exiting and re-joining entities could repeatedly get pulled ahead of it. Fix it by making the min_vruntime strictly monotonic by tracking it separately (an keeping it always up to date) instead of fetching what happens to be the top of the tree sorted by virtual runtime. While at it replace open coded ktime_t comparison with the correct ktime_after() helper. Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") Tested-by: Luke.Wildhardt@proton.me Cc: Christian König <christian.koenig@amd.com> Cc: Danilo Krummrich <dakr@kernel.org> Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost <matthew.brost@intel.com> Cc: Philipp Stanner <phasta@kernel.org> Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> > diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c > index 23f46ec610e78..bc8363974a276 100644 > --- a/drivers/gpu/drm/scheduler/sched_rq.c > +++ b/drivers/gpu/drm/scheduler/sched_rq.c [ ... ] > @@ -151,7 +130,7 @@ drm_sched_entity_save_vruntime(struct drm_sched_entity *entity, > > spin_lock(&stats->lock); > vruntime = stats->vruntime; > - if (min_vruntime && vruntime > min_vruntime) > + if (ktime_after(vruntime, min_vruntime)) [Severity: High] Will this condition always evaluate to false and unconditionally reset the saved lag to 0? In drm_sched_rq_pop_entity(), the runqueue's min_vruntime is updated to be at least the entity's current vruntime: drm_sched_rq_pop_entity() { ... ts = drm_sched_entity_update_vruntime(entity); if (ktime_after(ts, rq->min_vruntime)) rq->min_vruntime = ts; Later in the same function, if the queue is empty, we call: drm_sched_entity_save_vruntime(entity, rq->min_vruntime); Since min_vruntime is guaranteed to be greater than or equal to the entity's vruntime at this point, the ktime_after(vruntime, min_vruntime) check here seems mathematically impossible to satisfy. Does this mean entities that empty their runqueue will permanently lose their accumulated vruntime penalty? > vruntime = ktime_sub(vruntime, min_vruntime); > else > vruntime = 0; [ ... ] > @@ -351,27 +324,27 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) > * Update the entity's location in the min heap according to > * the timestamp of the next job, if any. > */ > + > + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { > + ts = drm_sched_entity_update_vruntime(entity); > + if (ktime_after(ts, rq->min_vruntime)) > + rq->min_vruntime = ts; > + } > + [Severity: Medium] Does this unintentionally advance the runqueue's min_vruntime past other entities still waiting in the RB tree? By setting rq->min_vruntime to the newly updated vruntime of the currently executing entity, it seems we might ignore the vruntimes of other entities that are still queued. When new entities join the runqueue, won't their initial vruntime be based on this artificially inflated min_vruntime, penalizing them relative to the existing waiting entities? > next_job = drm_sched_entity_queue_peek(entity); > if (next_job) { > - ktime_t ts; > - > - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) > - ts = drm_sched_entity_get_job_ts(entity); > - else if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) > + if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) > ts = next_job->submit_ts; > - else > + else if (drm_sched_policy == DRM_SCHED_POLICY_RR) > ts = drm_sched_rq_next_rr_ts(rq, entity); > > drm_sched_rq_update_fifo_locked(entity, rq, ts); > } else { > - drm_sched_rq_remove_fifo_locked(entity, rq); > - > - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { > - ktime_t min_vruntime; > + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) > + drm_sched_entity_save_vruntime(entity, > + rq->min_vruntime); > > - min_vruntime = drm_sched_rq_get_min_vruntime(rq); > - drm_sched_entity_save_vruntime(entity, min_vruntime); > - } > + drm_sched_rq_remove_fifo_locked(entity, rq); > } > > spin_unlock(&rq->lock); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260814075739.32481-1-tvrtko.ursulin@igalia.com?part=2 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime 2026-08-14 8:09 ` sashiko-bot @ 2026-08-14 10:34 ` Tvrtko Ursulin 0 siblings, 0 replies; 8+ messages in thread From: Tvrtko Ursulin @ 2026-08-14 10:34 UTC (permalink / raw) To: sashiko-reviews; +Cc: dri-devel On 14/08/2026 09:09, sashiko-bot@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: > - [High] drm_sched_entity_save_vruntime() always saves a virtual runtime lag of 0 due to an impossible condition, permanently destroying the entity's relative lag state. > - [Medium] rq->min_vruntime incorrectly tracks the maximum updated vruntime of the executing entity rather than the true minimum of the runqueue, penalizing newly enqueued entities. > -- > > --- Patch [2]: [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime --- > commit 840e24cc75ac9a836c633b23cb95dda0e0dc3b75 > Author: Tvrtko Ursulin <tvrtko.ursulin@igalia.com> > > drm/sched: Ensure monotonic min_vruntime > > min_vruntime handling had a bug where if an entity never exited the run- > queue it could get penalised by its virtual runtime only ever growing, > while the periodically exiting and re-joining entities could repeatedly > get pulled ahead of it. > > Fix it by making the min_vruntime strictly monotonic by tracking it > separately (an keeping it always up to date) instead of fetching what > happens to be the top of the tree sorted by virtual runtime. > > While at it replace open coded ktime_t comparison with the correct > ktime_after() helper. > > Fixes: 2fa4d8e2c109 ("drm/sched: Add fair scheduling policy") > Tested-by: Luke.Wildhardt@proton.me > Cc: Christian König <christian.koenig@amd.com> > Cc: Danilo Krummrich <dakr@kernel.org> > Cc: Luke.Wildhardt@proton.me > Cc: Matthew Brost <matthew.brost@intel.com> > Cc: Philipp Stanner <phasta@kernel.org> > Cc: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> > Cc: Vitaly Prosyak <vitaly.prosyak@amd.com> > >> diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c >> index 23f46ec610e78..bc8363974a276 100644 >> --- a/drivers/gpu/drm/scheduler/sched_rq.c >> +++ b/drivers/gpu/drm/scheduler/sched_rq.c > > [ ... ] > >> @@ -151,7 +130,7 @@ drm_sched_entity_save_vruntime(struct drm_sched_entity *entity, >> >> spin_lock(&stats->lock); >> vruntime = stats->vruntime; >> - if (min_vruntime && vruntime > min_vruntime) >> + if (ktime_after(vruntime, min_vruntime)) > > [Severity: High] > Will this condition always evaluate to false and unconditionally reset the > saved lag to 0? > > In drm_sched_rq_pop_entity(), the runqueue's min_vruntime is updated to be > at least the entity's current vruntime: > > drm_sched_rq_pop_entity() { > ... > ts = drm_sched_entity_update_vruntime(entity); > if (ktime_after(ts, rq->min_vruntime)) > rq->min_vruntime = ts; > > Later in the same function, if the queue is empty, we call: > > drm_sched_entity_save_vruntime(entity, rq->min_vruntime); > > Since min_vruntime is guaranteed to be greater than or equal to the entity's > vruntime at this point, the ktime_after(vruntime, min_vruntime) check here > seems mathematically impossible to satisfy. > > Does this mean entities that empty their runqueue will permanently lose > their accumulated vruntime penalty? Okay, it's not a quick fix so I'll drop 2/2 for now and come back to it later. 1/2 stands on it's own anyway. Regards, Tvrtko > >> vruntime = ktime_sub(vruntime, min_vruntime); >> else >> vruntime = 0; > > [ ... ] > >> @@ -351,27 +324,27 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) >> * Update the entity's location in the min heap according to >> * the timestamp of the next job, if any. >> */ >> + >> + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { >> + ts = drm_sched_entity_update_vruntime(entity); >> + if (ktime_after(ts, rq->min_vruntime)) >> + rq->min_vruntime = ts; >> + } >> + > > [Severity: Medium] > Does this unintentionally advance the runqueue's min_vruntime past other > entities still waiting in the RB tree? > > By setting rq->min_vruntime to the newly updated vruntime of the currently > executing entity, it seems we might ignore the vruntimes of other entities > that are still queued. > > When new entities join the runqueue, won't their initial vruntime be based > on this artificially inflated min_vruntime, penalizing them relative to the > existing waiting entities? > >> next_job = drm_sched_entity_queue_peek(entity); >> if (next_job) { >> - ktime_t ts; >> - >> - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) >> - ts = drm_sched_entity_get_job_ts(entity); >> - else if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) >> + if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) >> ts = next_job->submit_ts; >> - else >> + else if (drm_sched_policy == DRM_SCHED_POLICY_RR) >> ts = drm_sched_rq_next_rr_ts(rq, entity); >> >> drm_sched_rq_update_fifo_locked(entity, rq, ts); >> } else { >> - drm_sched_rq_remove_fifo_locked(entity, rq); >> - >> - if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { >> - ktime_t min_vruntime; >> + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) >> + drm_sched_entity_save_vruntime(entity, >> + rq->min_vruntime); >> >> - min_vruntime = drm_sched_rq_get_min_vruntime(rq); >> - drm_sched_entity_save_vruntime(entity, min_vruntime); >> - } >> + drm_sched_rq_remove_fifo_locked(entity, rq); >> } >> >> spin_unlock(&rq->lock); > ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-14 10:34 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-14 7:57 [PATCH 0/2] drm/sched: Fair policy fixups Tvrtko Ursulin 2026-08-14 7:57 ` [PATCH 1/2] drm/sched: Do not restore unsaved virtual runtime Tvrtko Ursulin 2026-08-14 8:10 ` sashiko-bot 2026-08-14 8:53 ` Matthew Brost 2026-08-14 9:58 ` Tvrtko Ursulin 2026-08-14 7:57 ` [PATCH 2/2] drm/sched: Ensure monotonic min_vruntime Tvrtko Ursulin 2026-08-14 8:09 ` sashiko-bot 2026-08-14 10:34 ` Tvrtko Ursulin
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.