From 4e28aa8f7ee26d67a4addee6e3980f1cbf861b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Sat, 4 Jul 2026 10:41:33 +0200 Subject: drm/drm_exec: avoid indirect goto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_exec component uses a variable with scope limited to the for() and an indirect goto to allow instantiating multiple macros in the same function. This unfortunately doesn't work well with certain compilers when the indirect goto can't be lowered to a direct jump. Switch the indirect goto to a direct goto, the drawback is that we now can't use the dma_exec_until_all_locked() macro in the same function multiple times. The is currently only one user of this and only as a hacky workaround which is about to be removed. So document that the __label__ statement should be used when the macro is used multiple times and fix the tests and the only use case where that is necessary. Suggested-by: Peter Zijlstra Signed-off-by: Christian König Fixes: 9920249a5288 ("drm/amdgpu: convert amdgpu_vm_lock_by_pasid() to drm_exec") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606231854.7LeCtlLe-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606232356.gwHMAJAW-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606240753.kYjobJVl-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202606241110.iUga5vVw-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607031446.1PWG18mN-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607031837.HSmBj8pr-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607040159.GopyEswS-lkp@intel.com/ Tested-by: Mikhail Gavrilov Reviewed-by: Dave Airlie Link: https://lore.kernel.org/r/20260704084133.122053-1-christian.koenig@amd.com --- include/drm/drm_exec.h | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index 8725ba92ff91..cc2937185a9f 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -101,17 +101,6 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) #define drm_exec_for_each_locked_object_reverse(exec, obj) \ __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) -/* - * Helper to drm_exec_until_all_locked(). Don't use directly. - * - * Since labels can't be defined local to the loop's body we use a jump pointer - * to make sure that the retry is only used from within the loop's body. - */ -#define __drm_exec_until_all_locked(exec, _label) \ -_label: \ - for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \ - drm_exec_cleanup(exec);) - /** * drm_exec_until_all_locked - loop until all GEM objects are locked * @exec: drm_exec object @@ -119,9 +108,18 @@ _label: \ * Core functionality of the drm_exec object. Loops until all GEM objects are * locked and no more contention exists. At the beginning of the loop it is * guaranteed that no GEM object is locked. + * + * A global label name drm_exec_retry is used, if you need to use more than one + * instance of this macro in the same function the label needs to be made local + * to the block with the __label__ keyword. */ #define drm_exec_until_all_locked(exec) \ - __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec)) + for (bool const __maybe_unused __drm_exec_loop = false; \ + drm_exec_cleanup(exec);) \ + if (false) { \ +drm_exec_retry: __maybe_unused; \ + continue; \ + } else /** * drm_exec_retry_on_contention - restart the loop to grap all locks @@ -129,12 +127,14 @@ _label: \ * * Control flow helper to continue when a contention was detected and we need to * clean up and re-start the loop to prepare all GEM objects. + * The __drm_exec_loop check exists to prevent usage outside of an + * drm_exec_until_all_locked() loop. */ #define drm_exec_retry_on_contention(exec) \ do { \ if (unlikely(drm_exec_is_contended(exec))) \ - goto *__drm_exec_retry_ptr; \ - } while (0) + goto drm_exec_retry; \ + } while (__drm_exec_loop) /** * drm_exec_is_contended - check for contention @@ -154,12 +154,14 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) * * Unconditionally retry the loop to lock all objects. For consistency, * the exec object needs to be newly initialized. + * The __drm_exec_loop check exists to prevent usage outside of an + * drm_exec_until_all_locked() loop. */ #define drm_exec_retry(_exec) \ do { \ WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ - goto *__drm_exec_retry_ptr; \ - } while (0) + goto drm_exec_retry; \ + } while (__drm_exec_loop) /** * drm_exec_ticket - return the ww_acquire_ctx for this exec context -- cgit v1.2.3 From a3fdf74ffa5966e5b4a17f1e9c5687f73bb0d536 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 16 Jul 2026 13:13:58 -0700 Subject: drm/ttm/pool: back up at native page order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ttm_pool_split_for_swap() unconditionally splits high-order pool pages into order-0 pages before backup, so every compound the shrinker touches is shattered even when the rest of the system would prefer it stay intact. Under sustained kswapd pressure this fragments memory enough to drive other parts of MM into recovery loops. Back up each compound at its native order instead. In ttm_pool_backup(), hand the full compound to the new ttm_backup_backup_folio(), which backs up subpages to a contiguous range of shmem indices and returns the base handle plus the number of subpages actually backed up (@nr_backed). On full success, free the compound once at its native order -- no split_page(), no per-4K refcount juggling. A per-folio backup can't be made fully atomic under memory pressure: ttm_backup_backup_folio() must allocate shmem folios before source subpages can be released, so under true OOM any subpage may fail while the rest of the compound is still live. Two mechanisms handle this without regressing reclaim behaviour: - alloc_gfp gets __GFP_NOMEMALLOC whenever order > 0 (cleared again for order-0), so a high-order backup fails fast with -ENOMEM instead of draining kernel reserves, leaving them for other allocations under the same pressure. - If ttm_backup_backup_folio() still returns a short @nr_backed with a valid handle for the successfully-backed prefix, split the source compound with ttm_pool_split_for_swap(), free the prefix as order-0 pages (already safely in shmem), and retry the remaining subpages at order 0, where __GFP_NOMEMALLOC is cleared and reserves may be used as a last resort. This preserves the original split-on-OOM fallback while keeping the common case fragmentation-free, and preserves the "partial backup is allowed" contract (shrunken is incremented per subpage backed up). The restore-side leftover-page split in ttm_pool_restore_commit() is left as-is: it's unreachable in practice and not worth complicating the restore state machine to avoid. Testing: the existing backup_fault_inject point only truncated tt->num_pages, which never exercised the reactive split path above since it never left a compound partially backed up. Wire fault injection into ttm_backup_backup_folio() itself: past the first subpage of a compound, synthesize a -ENOMEM in place of shmem_read_folio_gfp() when should_fail() trips, producing the same short @nr_pages_backed a real failure would and forcing ttm_pool_backup() through the split-and-retry path. The fault_attr stays private to ttm_pool.c; ttm_backup.c reaches it through ttm_backup_fault_inject_folio(), declared in ttm_pool_internal.h. While converting the writeback branch to operate on the whole folio, the unlock condition after shmem_writeout() also changed from `if (ret)` to `if (ret == AOP_WRITEPAGE_ACTIVATE)`, matching the actual contract: shmem_writeout()/swap_writeout() only leave the folio locked when returning AOP_WRITEPAGE_ACTIVATE; any other return (including a hard error from arch_prepare_to_swap()) means the folio was already unlocked internally. The old `if (ret)` check would have double- unlocked in that hard-error case. Cc: Christian Koenig Cc: Huang Rui Cc: Matthew Auld Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: b63d715b8090 ("drm/ttm/pool, drm/ttm/tt: Provide a helper to shrink pages") Suggested-by: Thomas Hellström Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260716201358.4086085-1-matthew.brost@intel.com --- include/drm/ttm/ttm_backup.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include/drm') diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h index 29b9c855af77..49efa713e87c 100644 --- a/include/drm/ttm/ttm_backup.h +++ b/include/drm/ttm/ttm_backup.h @@ -13,9 +13,8 @@ * ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer * @handle: The handle to convert. * - * Converts an opaque handle received from the - * ttm_backup_backup_page() function to an (invalid) - * struct page pointer suitable for a struct page array. + * Converts an opaque handle received from a ttm_backup_backup_*() + * function to an (invalid) struct page pointer suitable for a struct page array. * * Return: An (invalid) struct page pointer. */ @@ -59,9 +58,10 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, pgoff_t handle, bool intr, gfp_t additional_gfp); s64 -ttm_backup_backup_page(struct file *backup, struct page *page, - bool writeback, pgoff_t idx, gfp_t page_gfp, - gfp_t alloc_gfp); +ttm_backup_backup_folio(struct file *backup, struct folio *folio, + unsigned int order, bool writeback, pgoff_t idx, + gfp_t folio_gfp, gfp_t alloc_gfp, + pgoff_t *nr_pages_backed); void ttm_backup_fini(struct file *backup); -- cgit v1.2.3 From bad177fa75e607e396cd57daaaed881450d7a471 Mon Sep 17 00:00:00 2001 From: Alessandro Rinaldi Date: Fri, 26 Jun 2026 16:36:00 +0200 Subject: drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05 The Lenovo Legion 5 15ARH05 (Renoir) ships a BOE 0x08DF eDP panel that advertises AUX/DPCD backlight control, so amdgpu's automatic detection (amdgpu_backlight == -1) selects AUX. On this panel the AUX backlight path has no effect: brightness writes are accepted but the panel level never changes, the display is stuck at a fixed brightness and max_brightness is reported as a bogus 511000. As a result neither the desktop brightness slider nor the brightness hotkeys do anything. Forcing PWM backlight (amdgpu.backlight=0) restores working control: max_brightness becomes 65535 and the level tracks writes. This has long been applied by users as a manual kernel-parameter workaround. Extend the generic panel backlight quirk with a force_pwm flag, add an entry for the Legion 5 15ARH05 / BOE 0x08DF panel, and have amdgpu disable AUX backlight (use PWM) when the quirk matches and the user lets the driver auto-select the backlight type. Signed-off-by: Alessandro Rinaldi Tested-by: Alessandro Rinaldi Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 81b39f43e7e53589491e2eef6bad5389626b4b9c) Cc: stable@vger.kernel.org --- include/drm/drm_utils.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/drm') diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h index 6a46f755daba..7e077484c5bb 100644 --- a/include/drm/drm_utils.h +++ b/include/drm/drm_utils.h @@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height); struct drm_panel_backlight_quirk { u16 min_brightness; u32 brightness_mask; + bool force_pwm; }; const struct drm_panel_backlight_quirk * -- cgit v1.2.3 From 6853da8cdb6676d8b173fac15094490dff419ac5 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:20 +0100 Subject: Revert "drm/sched: Remove drm_sched_init_args->num_rqs" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d09339388b778f04dd9e638befa2594c9cb4290b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-2-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/drm') diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index d61c19e78182..f1e15a80c844 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -608,6 +608,8 @@ struct drm_gpu_scheduler { * @ops: backend operations provided by the driver * @submit_wq: workqueue to use for submission. If NULL, an ordered wq is * allocated and used. + * @num_rqs: Number of run-queues. This may be at most DRM_SCHED_PRIORITY_COUNT, + * as there's usually one run-queue per priority, but may be less. * @credit_limit: the number of credits this scheduler can hold from all jobs * @hang_limit: number of times to allow a job to hang before dropping it. * This mechanism is DEPRECATED. Set it to 0. @@ -621,6 +623,7 @@ struct drm_sched_init_args { const struct drm_sched_backend_ops *ops; struct workqueue_struct *submit_wq; struct workqueue_struct *timeout_wq; + u32 num_rqs; u32 credit_limit; unsigned int hang_limit; long timeout; -- cgit v1.2.3 From 67cf83ac8316ab6a866cbafc449069409b11f923 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:35 +0100 Subject: Revert "drm/sched: Embed run queue singleton into the scheduler" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 16e7698bc04d3dd19d95a688e4b0297a0e28a93b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-17-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index f1e15a80c844..80e5ae2d61ec 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -249,6 +249,7 @@ struct drm_sched_entity { /** * struct drm_sched_rq - queue of entities to be scheduled. * + * @sched: the scheduler to which this rq belongs to. * @lock: protects @entities, @rb_tree_root and @head_prio. * @entities: list of the entities to be scheduled. * @rb_tree_root: root of time based priority queue of entities for FIFO scheduling @@ -259,6 +260,8 @@ struct drm_sched_entity { * the next entity to emit commands from. */ struct drm_sched_rq { + struct drm_gpu_scheduler *sched; + spinlock_t lock; /* Following members are protected by the @lock: */ struct list_head entities; @@ -581,7 +584,7 @@ struct drm_gpu_scheduler { atomic_t credit_count; long timeout; const char *name; - struct drm_sched_rq rq; + struct drm_sched_rq *rq; wait_queue_head_t job_scheduled; atomic64_t job_id_count; struct workqueue_struct *submit_wq; -- cgit v1.2.3 From 9e9da8625427a80e1246f2df0f3ac18374045892 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:36 +0100 Subject: Revert "drm/sched: Remove FIFO and RR and simplify to a single run queue" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 77a6809f1dc39376116f8d769a0d2630dc95ad79. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-18-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 80e5ae2d61ec..790f7ecb6b85 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -100,7 +100,8 @@ struct drm_sched_entity { * @lock: * * Lock protecting the run-queue (@rq) to which this entity belongs, - * @priority and the list of schedulers (@sched_list, @num_sched_list). + * @priority, the list of schedulers (@sched_list, @num_sched_list) and + * the @rr_ts field. */ spinlock_t lock; @@ -153,6 +154,18 @@ struct drm_sched_entity { */ enum drm_sched_priority priority; + /** + * @rq_priority: Run-queue priority + */ + enum drm_sched_priority rq_priority; + + /** + * @rr_ts: + * + * Fake timestamp of the last popped job from the entity. + */ + ktime_t rr_ts; + /** * @job_queue: the list of jobs of this entity. */ @@ -250,7 +263,8 @@ struct drm_sched_entity { * struct drm_sched_rq - queue of entities to be scheduled. * * @sched: the scheduler to which this rq belongs to. - * @lock: protects @entities, @rb_tree_root and @head_prio. + * @lock: protects @entities, @rb_tree_root, @rr_ts and @head_prio. + * @rr_ts: monotonically incrementing fake timestamp for RR mode. * @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. @@ -264,6 +278,7 @@ struct drm_sched_rq { spinlock_t lock; /* Following members are protected by the @lock: */ + ktime_t rr_ts; struct list_head entities; struct rb_root_cached rb_tree_root; enum drm_sched_priority head_prio; @@ -349,6 +364,13 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * to schedule the job. */ struct drm_sched_job { + /** + * @submit_ts: + * + * When the job was pushed into the entity queue. + */ + ktime_t submit_ts; + /** * @sched: * @@ -552,7 +574,11 @@ struct drm_sched_backend_ops { * @credit_count: the current credit count of this scheduler * @timeout: the time after which a job is removed from the scheduler. * @name: name of the ring for which this scheduler is being used. - * @rq: Scheduler run queue. + * @num_user_rqs: Number of run-queues. This is at most + * DRM_SCHED_PRIORITY_COUNT, as there's usually one run-queue per + * priority, but could be less. + * @num_rqs: Equal to @num_user_rqs for FIFO and RR and 1 for the FAIR policy. + * @sched_rq: An allocated array of run-queues of size @num_rqs; * @job_scheduled: once drm_sched_entity_flush() is called the scheduler * waits on this wait queue until all the scheduled jobs are * finished. @@ -584,7 +610,9 @@ struct drm_gpu_scheduler { atomic_t credit_count; long timeout; const char *name; - struct drm_sched_rq *rq; + u32 num_rqs; + u32 num_user_rqs; + struct drm_sched_rq **sched_rq; wait_queue_head_t job_scheduled; atomic64_t job_id_count; struct workqueue_struct *submit_wq; -- cgit v1.2.3