From 40a22ad1b5cb86a27f54d7837be20113d0eb49d2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 10 Jun 2026 17:18:21 +0200 Subject: drm/appletbdrm: Allocate request/response buffers in begin_fb_access In atomic_check, damage handling is not fully evaluated. Another atomic_check helper could trigger a full modeset and thus invalidate damage clips. Allocation of the request/response buffers in appletbdrm depends on correct damage information. Otherwise it might allocate incorrectly sized buffers. Allocate the buffers in the driver's begin_fb_access helper. It runs early during the commit when damage clipping has been fully evaluated. v5: - pass plane state as the old damage-iterator state v2: - allocate before drm_gem_begin_shadow_fb_access() to avoid leak on error Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Acked-by: Aditya Garg Acked-by: Zack Rusin Link: https://patch.msgid.link/20260610152505.260172-6-tzimmermann@suse.de --- drivers/gpu/drm/tiny/appletbdrm.c | 53 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index cdd35af49892..ef71b9957961 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = { DRM_FORMAT_XRGB8888, /* emulated */ }; -static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, - struct drm_atomic_commit *state) +static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *new_plane_state) { - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); - struct drm_crtc *new_crtc = new_plane_state->crtc; - struct drm_crtc_state *new_crtc_state = NULL; struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state); + size_t frames_size = 0; struct drm_atomic_helper_damage_iter iter; struct drm_rect damage; - size_t frames_size = 0; size_t request_size; - int ret; - - if (new_crtc) - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); - ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, - DRM_PLANE_NO_SCALING, - DRM_PLANE_NO_SCALING, - false, false); - if (ret) - return ret; - else if (!new_plane_state->visible) - return 0; - - drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state); + drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage)); } @@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, appletbdrm_state->request_size = request_size; appletbdrm_state->frames_size = frames_size; + return drm_gem_begin_shadow_fb_access(plane, new_plane_state); +} + +static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_commit *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + return 0; } @@ -468,7 +474,7 @@ end_fb_cpu_access: } static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, - struct drm_atomic_commit *old_state) + struct drm_atomic_commit *old_state) { struct appletbdrm_device *adev = drm_to_adev(plane->dev); struct drm_device *drm = plane->dev; @@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane, } static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access, + .end_fb_access = drm_gem_end_shadow_fb_access, .atomic_check = appletbdrm_primary_plane_helper_atomic_check, .atomic_update = appletbdrm_primary_plane_helper_atomic_update, .atomic_disable = appletbdrm_primary_plane_helper_atomic_disable, -- cgit v1.3.1 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 --- drivers/gpu/drm/ttm/ttm_backup.c | 129 +++++++++++++++++++++----------- drivers/gpu/drm/ttm/ttm_pool.c | 89 ++++++++++++++++++---- drivers/gpu/drm/ttm/ttm_pool_internal.h | 8 ++ include/drm/ttm/ttm_backup.h | 12 +-- 4 files changed, 173 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c index 81df4cb5606b..3c067aadc52d 100644 --- a/drivers/gpu/drm/ttm/ttm_backup.c +++ b/drivers/gpu/drm/ttm/ttm_backup.c @@ -6,9 +6,10 @@ #include #include -#include #include +#include "ttm_pool_internal.h" + /* * Need to map shmem indices to handle since a handle value * of 0 means error, following the swp_entry_t convention. @@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, } /** - * ttm_backup_backup_page() - Backup a page + * ttm_backup_backup_folio() - Backup a folio * @backup: The struct backup pointer to use. - * @page: The page to back up. - * @writeback: Whether to perform immediate writeback of the page. + * @folio: The folio to back up. + * @order: The allocation order of @folio. Since TTM allocates higher-order + * pages without __GFP_COMP, folio_nr_pages(@folio) would always + * return 1; the caller must pass the true order explicitly. + * @writeback: Whether to perform immediate writeback of the folio's pages. * This may have performance implications. - * @idx: A unique integer for each page and each struct backup. + * @idx: A unique integer for the first page of the folio and each struct backup. * This allows the backup implementation to avoid managing * its address space separately. - * @page_gfp: The gfp value used when the page was allocated. - * This is used for accounting purposes. + * @folio_gfp: The gfp value used when the folio was allocated. + * Currently unused. * @alloc_gfp: The gfp to be used when allocating memory. + * @nr_pages_backed: Output. On a successful return, set to the number of + * pages actually backed up, which may be less than (1 << @order) + * if an -ENOMEM was encountered mid-folio. * * Context: If called from reclaim context, the caller needs to * assert that the shrinker gfp has __GFP_FS set, to avoid @@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, * that the shrinker gfp has __GFP_IO set, since without it, * we're not allowed to start backup IO. * - * Return: A handle on success. Negative error code on failure. - * - * Note: This function could be extended to back up a folio and - * implementations would then split the folio internally if needed. - * Drawback is that the caller would then have to keep track of - * the folio size- and usage. + * Return: A handle for the first backed-up page on success (handles for + * subsequent pages follow sequentially). -ENOMEM if no pages could be backed + * up. Any other negative error code if a non-ENOMEM failure occurred; in that + * case any pages backed up so far are truncated before returning. */ 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) { struct address_space *mapping = backup->f_mapping; - unsigned long handle = 0; + int nr_pages = 1 << order; struct folio *to_folio; - int ret; - - to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp); - if (IS_ERR(to_folio)) - return PTR_ERR(to_folio); - - folio_mark_accessed(to_folio); - folio_lock(to_folio); - folio_mark_dirty(to_folio); - copy_highpage(folio_file_page(to_folio, idx), page); - handle = ttm_backup_shmem_idx_to_handle(idx); - - if (writeback && !folio_mapped(to_folio) && - folio_clear_dirty_for_io(to_folio)) { - folio_set_reclaim(to_folio); - ret = shmem_writeout(to_folio, NULL, NULL); - if (!folio_test_writeback(to_folio)) - folio_clear_reclaim(to_folio); + int ret, i; + + *nr_pages_backed = 0; + + for (i = 0; i < nr_pages; ) { + int to_nr, j; + /* - * If writeout succeeds, it unlocks the folio. errors - * are otherwise dropped, since writeout is only best - * effort here. + * Only inject past the first subpage so *nr_pages_backed is + * always > 0 here, matching a genuine mid-compound -ENOMEM + * and driving the caller's reactive split fallback instead + * of an early, no-progress failure. */ - if (ret) + if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i && + ttm_backup_fault_inject_folio()) + to_folio = ERR_PTR(-ENOMEM); + else + to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp); + if (IS_ERR(to_folio)) { + int err = PTR_ERR(to_folio); + + if (err == -ENOMEM && *nr_pages_backed) + return ttm_backup_shmem_idx_to_handle(idx); + + if (*nr_pages_backed) { + shmem_truncate_range(file_inode(backup), + (loff_t)idx << PAGE_SHIFT, + ((loff_t)(idx + i) << PAGE_SHIFT) - 1); + /* + * The pages just truncated are no longer + * backed up; don't let the caller mistake + * them for valid handles. + */ + *nr_pages_backed = 0; + } + return err; + } + + to_nr = min_t(int, nr_pages - i, + folio_next_index(to_folio) - (idx + i)); + + folio_mark_accessed(to_folio); + folio_lock(to_folio); + folio_mark_dirty(to_folio); + + for (j = 0; j < to_nr; j++) + copy_highpage(folio_file_page(to_folio, idx + i + j), + folio_page(folio, i + j)); + + if (writeback && !folio_mapped(to_folio) && + folio_clear_dirty_for_io(to_folio)) { + folio_set_reclaim(to_folio); + ret = shmem_writeout(to_folio, NULL, NULL); + if (!folio_test_writeback(to_folio)) + folio_clear_reclaim(to_folio); + if (ret == AOP_WRITEPAGE_ACTIVATE) + folio_unlock(to_folio); + } else { folio_unlock(to_folio); - } else { - folio_unlock(to_folio); - } + } - folio_put(to_folio); + folio_put(to_folio); + i += to_nr; + *nr_pages_backed = i; + } - return handle; + return ttm_backup_shmem_idx_to_handle(idx); } /** diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 46983e7de7a3..e7648ad3e615 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -53,8 +53,23 @@ #ifdef CONFIG_FAULT_INJECTION #include static DECLARE_FAULT_ATTR(backup_fault_inject); + +/* + * Exposed to ttm_backup.c so a mid-compound subpage can be made to fail + * with -ENOMEM, exercising the reactive split-and-retry fallback in + * ttm_pool_backup() for high-order backups. + */ +bool ttm_backup_fault_inject_folio(void) +{ + return should_fail(&backup_fault_inject, 1); +} #else #define should_fail(...) false + +bool ttm_backup_fault_inject_folio(void) +{ + return false; +} #endif /** @@ -487,7 +502,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p) /** * DOC: Partial backup and restoration of a struct ttm_tt. * - * Swapout using ttm_backup_backup_page() and swapin using + * Swapout using ttm_backup_backup_folio() and swapin using * ttm_backup_copy_page() may fail. * The former most likely due to lack of swap-space or memory, the latter due * to lack of memory or because of signal interruption during waits. @@ -1036,12 +1051,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, { struct file *backup = tt->backup; struct page *page; - unsigned long handle; gfp_t alloc_gfp; gfp_t gfp; int ret = 0; pgoff_t shrunken = 0; - pgoff_t i, num_pages; + pgoff_t i, j, num_pages, npages; + pgoff_t nr_backed; if (WARN_ON(ttm_tt_is_backed_up(tt))) return -EINVAL; @@ -1119,9 +1134,11 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1)) num_pages = DIV_ROUND_UP(num_pages, 2); - for (i = 0; i < num_pages; ++i) { - s64 shandle; + for (i = 0; i < num_pages; i += npages) { + unsigned int order; + s64 handle; + npages = 1; page = tt->pages[i]; if (unlikely(!page)) continue; @@ -1130,19 +1147,61 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (unlikely(ttm_backup_page_ptr_is_handle(page))) continue; - ttm_pool_split_for_swap(pool, page); + order = ttm_pool_page_order(pool, page); + npages = 1UL << order; + + /* + * We don't allow dipping kernel reserves for high order backup + */ + if (order) + alloc_gfp |= __GFP_NOMEMALLOC; + else + alloc_gfp &= ~__GFP_NOMEMALLOC; + + /* + * Back up the compound atomically at its native order. If + * fault injection truncated num_pages mid-compound, skip + * the partial tail rather than splitting. + */ + if (unlikely(i + npages > num_pages)) + break; - shandle = ttm_backup_backup_page(backup, page, flags->writeback, i, - gfp, alloc_gfp); - if (shandle < 0) { - /* We allow partially shrunken tts */ - ret = shandle; + handle = ttm_backup_backup_folio(backup, page_folio(page), + order, flags->writeback, i, + gfp, alloc_gfp, + &nr_backed); + /* + * Zero progress on this compound (whether order 0 or a + * high-order compound that failed before backing up even + * its first subpage) is unrecoverable: bail out rather than + * looping forever with npages == nr_backed == 0 below. + */ + if (unlikely(handle < 0 && !nr_backed)) { + ret = handle; break; } - handle = shandle; - tt->pages[i] = ttm_backup_handle_to_page_ptr(handle); - __free_pages_gpu_account(page, 0, false); - shrunken++; + + for (j = 0; j < nr_backed; j++) + tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j); + + shrunken += nr_backed; + + if (unlikely(nr_backed < npages)) { + /* + * Partial OOM backup: split the compound and free the + * subpages whose content is now in shmem. Continue the + * loop from the first un-backed order-0 page. + */ + ttm_pool_split_for_swap(pool, page); + for (j = 0; j < nr_backed; j++) + __free_pages_gpu_account(page + j, 0, false); + npages = nr_backed; + continue; + } + + /* Fully backed up: free at native order. */ + page->private = 0; + __free_pages_gpu_account(page, order, false); } return shrunken ? shrunken : ret; diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h index 24c179fd69d1..cbb17a2129fe 100644 --- a/drivers/gpu/drm/ttm/ttm_pool_internal.h +++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h @@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool) return pool->alloc_flags & 0xff; } +/* + * Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault + * should be injected mid-compound to test the reactive split-and-retry + * fallback in ttm_pool_backup(). Always returns false when + * CONFIG_FAULT_INJECTION is disabled. + */ +bool ttm_backup_fault_inject_folio(void); + #endif 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.3.1 From 488f4902e1deba4d507b4b8c25547a366f5dac63 Mon Sep 17 00:00:00 2001 From: Wendy Liang Date: Sat, 18 Jul 2026 01:34:09 -0700 Subject: accel/amdxdna: Fix command timeout race When two commands enter aie2_sched_job_timedout() concurrently, both check the timeout detection state. The first scheduler thread observes tdr_status as SIGNALED and updates it to WAIT. The second thread then observes the updated state instead of the original SIGNALED state, which may cause the command timeout to be handled incorrectly. Replace tdr_status with last_signal_ts, which records the timestamp of the last driver signal. Timeout detection now only reads last_signal_ts and never modifies it, allowing multiple serialized detect() calls under dev_lock to evaluate the same signal timestamp independently. If there is not any new job scheduled or completed within tdr_timeout_ms, the command will timeout. Fixes: 9022f010977f ("accel/amdxdna: Check for device hang on job timeout") Signed-off-by: Wendy Liang Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260718083409.1825940-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 26 ++++++++++++++++++-------- drivers/accel/amdxdna/aie2_pci.c | 1 + drivers/accel/amdxdna/aie2_pci.h | 7 +------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 101f324ee178..4b3a62aa8798 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -43,20 +43,22 @@ struct aie2_ctx_health { static inline void aie2_tdr_signal(struct amdxdna_dev *xdna) { - WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED); + WRITE_ONCE(xdna->dev_handle->last_signal_ts, jiffies); } static bool aie2_tdr_detect(struct amdxdna_dev *xdna) { struct amdxdna_dev_hdl *ndev = xdna->dev_handle; + unsigned long last = READ_ONCE(ndev->last_signal_ts); - if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) { - XDNA_ERR(xdna, "TDR timeout detected"); - return true; - } + if (!tdr_timeout_ms) + return false; + + if (!time_after(jiffies, last + msecs_to_jiffies(tdr_timeout_ms))) + return false; - WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT); - return false; + XDNA_ERR(xdna, "TDR timeout detected"); + return true; } static void aie2_cmd_release(struct kref *ref) @@ -434,6 +436,12 @@ out: mmput(job->mm); fence = ERR_PTR(ret); } else { + /* + * Command is successfully posted to hardware, update the + * tdr timestamp. The total pending commands are limited. + * So there will not be a case that driver keeps posting + * commands without getting any hardware respond. + */ aie2_tdr_signal(hwctx->client->xdna); } trace_xdna_job(sched_job, hwctx->name, "sent to device", @@ -658,7 +666,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) const struct drm_sched_init_args args = { .ops = &sched_ops, .credit_limit = HWCTX_MAX_CMDS, - .timeout = msecs_to_jiffies(tdr_timeout_ms), + .timeout = tdr_timeout_ms ? + msecs_to_jiffies(tdr_timeout_ms) : + MAX_SCHEDULE_TIMEOUT, .name = "amdxdna_js", .dev = xdna->ddev.dev, }; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 22f66c7f534d..daec1f6b4907 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -420,6 +420,7 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto stop_fw; } + WRITE_ONCE(ndev->last_signal_ts, jiffies); ndev->dev_status = AIE2_DEV_START; return 0; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 77648cc548b6..ea1dac106400 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -143,11 +143,6 @@ struct aie2_exec_msg_ops { u32 (*get_chain_msg_op)(u32 cmd_op); }; -enum aie2_tdr_status { - AIE2_TDR_WAIT, - AIE2_TDR_SIGNALED, -}; - struct amdxdna_dev_hdl { struct aie_device aie; const struct amdxdna_dev_priv *priv; @@ -179,7 +174,7 @@ struct amdxdna_dev_hdl { u32 hwctx_num; struct amdxdna_async_error last_async_err; - enum aie2_tdr_status tdr_status; + unsigned long last_signal_ts; }; struct aie2_hw_ops { -- cgit v1.3.1 From a411ea4a87162898d2a0547fdfb721ddb7626be3 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Thu, 16 Jul 2026 08:39:59 +0530 Subject: drm/i915/backlight: Remove DP_EDP_BACKLIGHT_AUX_ENABLE_CAP check for DPCD backlight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out some panels allow only AUX based backlight by just setting the DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP and not setting the DP_EDP_BACKLIGHT_AUX_ENABLE_CAP. If we make DP_EDP_BACKLIGHT_AUX_ENABLE_CAP a necessity for AUX based DPCD backlight these panels loose the ability to manipulate backlight via AUX, especially ones with no PWM controller. Remove this check from function so that panels who do not advertise DP_EDP_BACKLIGHT_AUX_ENABLE_CAP but advertise DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP are able to manipulate backlight again. Fixes: ed8be780bdbc ("drm/i915/backlight: Fix VESA backlight possible check condition") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16507 Signed-off-by: Suraj Kandpal Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260716030959.436430-1-suraj.kandpal@intel.com (cherry picked from commit 7d594b24c915afb4b0c5fb8875403253daef5b24) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 7a6c07f6aaeb..266e042e0023 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp) int ret; u8 bit_min, bit_max; - /* - * Since we only support Fully AUX Based VESA Backlight interface make sure - * backlight enable is possible via AUX along with backlight adjustment - */ - if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP && - intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) return false; ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); -- cgit v1.3.1 From 17e2030f37600994440f875dc410615d5c66ee6d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 14 Jul 2026 15:36:41 +0800 Subject: drm/imagination: acquire vm_ctx->lock before mapping memory to GPU VM The drm gpuvm code doesn't protect find operation against map operation, and the driver needs to ensure a map operation shouldn't happen when a find operation is in progress. In some cases a find operation will be in progress when doing map/unmap operations, and the find operation will do a NULL pointer dereference. An example of the stack trace of such NULL dereference is shown below: ``` Unable to handle kernel access to user memory without uaccess routines at virtual address 0000000000000010 [] drm_gpuva_find+0x28/0x6c [drm_gpuvm] [] pvr_vm_unmap+0x34/0x68 [powervr] [] pvr_ioctl_vm_unmap+0x2e/0x50 [powervr] [] drm_ioctl_kernel+0x8e/0xdc [] drm_ioctl+0x1be/0x3e0 [] __riscv_sys_ioctl+0xba/0xc4 [] do_trap_ecall_u+0x23e/0x3f4 [] handle_exception+0x168/0x174 ``` As all occurences of drm_gpuva_find*() are already guarded by vm_ctx->lock, make pvr_vm_map() to acquire this lock to prevent disturbing any find operation. This fixes the NULL deference problem in drm_gpuva_find*(). Cc: stable@vger.kernel.org Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Fixes: 4bc736f890ce ("drm/imagination: vm: make use of GPUVM's drm_exec helper") Signed-off-by: Icenowy Zheng Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260714073641.1935075-1-zhengxingda@iscas.ac.cn Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index 396d349fb6ce..ceb78694cd98 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, pvr_gem_object_get(pvr_obj); + mutex_lock(&vm_ctx->lock); err = drm_gpuvm_exec_lock(&vm_exec); if (err) goto err_cleanup; @@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, drm_gpuvm_exec_unlock(&vm_exec); err_cleanup: + mutex_unlock(&vm_ctx->lock); pvr_vm_bind_op_fini(&bind_op); return err; -- cgit v1.3.1 From 57441577bac3637473da2c9644336eaa0ac5732f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 26 May 2026 19:24:47 +0530 Subject: drm/xe/madvise: Skip invalidation for purgeable state updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purgeable state updates only change VMA/BO metadata. They do not zap PTEs when switching between DONTNEED and WILLNEED. PTEs are zapped later if the BO is actually purged. xe_vm_invalidate_madvise_range() waits on the VM dma-resv before checking vma->skip_invalidation. Since purgeable madvise marks all affected VMAs to skip invalidation, this wait is unnecessary and can stall on unrelated in-flight work. Skip the invalidate path entirely for purgeable state updates. v2: - Replace inline 'args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE' check with a small helper madvise_range_needs_invalidation(). (Himal) Suggested-by: Matthew Brost Cc: Matthew Brost Cc: Thomas Hellström Cc: Himal Prasad Ghimiray Signed-off-by: Arvind Yadav Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260526135447.2973029-1-arvind.yadav@intel.com Signed-off-by: Tejas Upadhyay Fixes: ada7486c5668 ("drm/xe: Implement madvise ioctl for xe") Cc: # v6.18+ (cherry picked from commit 134377098b9c14abd31c3bcac00c9653f0f0c4c3) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm_madvise.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 246fe1843142..0474768a38aa 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end) return err; } +/** + * madvise_range_needs_invalidation() - Check whether madvise needs invalidation + * @args: madvise ioctl arguments + * + * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are + * zapped only if the BO is later purged. + * + * Return: true when the update needs PTE invalidation. + */ +static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args) +{ + return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE; +} + static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args) { if (XE_IOCTL_DBG(xe, !args)) @@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, - madvise_range.addr + args->range); + if (madvise_range_needs_invalidation(args)) + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); -- cgit v1.3.1 From 847b371debf3c8c72384ab7b9a0c4123a74cc925 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 14 Jul 2026 10:00:25 -0700 Subject: drm/gpusvm: Fix MM reference leak in drm_gpusvm_range_evict If kvmalloc_array() fails in drm_gpusvm_range_evict(), the MM reference acquired earlier is not released, resulting in a reference leak. Fix this by dropping the MM reference on the kvmalloc_array() failure path. Fixes: 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260714170025.3487974-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_gpusvm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 9a06ff7d2608..9aeaca02e928 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1753,8 +1753,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm, return -EFAULT; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + if (!pfns) { + mmput(mm); return -ENOMEM; + } hmm_range.hmm_pfns = pfns; while (!time_after(jiffies, timeout)) { -- cgit v1.3.1 From 67b8bfd4ec7dac6e79a7ad9ad19a7a9d6fc35a26 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Tue, 14 Jul 2026 15:54:32 -0700 Subject: drm/gpusvm: Zero HMM PFNs before scanning ranges drm_gpusvm_scan_mm() asks HMM to report the current CPU page-table state without faulting missing entries by leaving default_flags set to zero. The HMM PFN array is still caller-owned input/output state, and the framework may preserve input bits while filling entries. It is not safe for the caller to hand HMM an uninitialized array and then treat entries without HMM_PFN_VALID as an authoritative unpopulated result. Use kvcalloc() for the temporary PFN array so entries that are not reported as valid start from the documented zero state. This prevents random stack or heap contents from being interpreted as HMM PFN flags or PFN values during the scan. Fixes: f1d08a586482 ("drm/gpusvm: Introduce a function to scan the current migration state") Cc: stable@vger.kernel.org Signed-off-by: Stanislav Kinsburskii Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/178406967042.1113483.2116704310277917086.stgit@skinsburskii --- drivers/gpu/drm/drm_gpusvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 9aeaca02e928..18ddef94da47 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -781,7 +781,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range, const struct dev_pagemap *other = NULL; int err, i; - pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); + pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL); if (!pfns) return DRM_GPUSVM_SCAN_UNPOPULATED; -- cgit v1.3.1 From 0c9e6367639548307d3f578f6943ce72c9d39087 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Tue, 21 Jul 2026 09:15:58 +0800 Subject: drm/vc4: Prevent shader BO mappings from becoming writable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc4_gem_object_mmap() rejects a writable mapping of a validated shader BO, but leaves VM_MAYWRITE set. Userspace can map the BO read-only and then turn it writable with mprotect(). Validated shader BOs must stay read-only: the validator checks the instructions once and the GPU trusts them afterwards. A writable mapping lets userspace rewrite the code after validation, bypassing the validator. Clear VM_MAYWRITE on the read-only path so the mapping cannot be upgraded, as i915 already does for its read-only objects. Fixes: 463873d57014 ("drm/vc4: Add an API for creating GPU shaders in GEM BOs.") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260720085554.B0AF01F000E9@smtp.kernel.org/ Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260721011558.1672477-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_bo.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 2161761b1f22..5e7c46dd7823 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -732,9 +732,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct { struct vc4_bo *bo = to_vc4_bo(obj); - if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { - DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); - return -EINVAL; + if (bo->validated_shader) { + if (vma->vm_flags & VM_WRITE) { + DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); + return -EINVAL; + } + + vm_flags_clear(vma, VM_MAYWRITE); } mutex_lock(&bo->madv_lock); -- cgit v1.3.1 From a4f55260f7f7d4dc4d0ee55063dfb0c457b77991 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 23 Jun 2026 14:33:14 -0500 Subject: drm/vmwgfx: Validate vmw_surface_metadata::array_size This field comes from userspace and should be validated against specific limits depending on which Shader Model (SM) is available. Fixes: 504901dbb0b5 ("drm/vmwgfx: Refactor surface_define to use vmw_surface_metadata") Reported-by: Zero Day Initiative Cc: stable@vger.kernel.org Signed-off-by: Ian Forbes Reviewed-by: Maaz Mombasawala Signed-off-by: Zack Rusin Link: https://patch.msgid.link/20260623193314.506257-1-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index b2d3927b5567..bd0563741e89 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, static int vmw_gb_surface_destroy(struct vmw_resource *res); static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv); static int @@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, */ static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv) { @@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev, req->base.svga3d_flags); /* array_size must be null for non-GL3 host. */ - if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { - VMW_DEBUG_USER("SM4 surface not supported.\n"); - return -EINVAL; + if (req->base.array_size > 0) { + if (has_sm5_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else if (has_sm4_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else { + VMW_DEBUG_USER("SM4+ surface not supported.\n"); + return -EINVAL; + } } if (!has_sm4_1_context(dev_priv)) { -- cgit v1.3.1 From 18a551482a4a326790698b273e76d7575a51a57d Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 20 Jul 2026 18:14:47 -0500 Subject: accel: ethosu: Fix element size accounting for cmd stream validation There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits. For NHWC format, the element size is simply missing from the calculation. The bitfield for the element size is different between IFM/IFM2 and OFM. IFM and IFM2 encode the precision in parameter bits 2:3, while OFM uses bits 1:2. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Acked-by: Tomeu Vizoso Link: https://patch.msgid.link/20260720231450.485221-1-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 3401883e207f..1daff32610c7 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, struct feat_matrix *fm, - u32 x, u32 y, u32 c) + u32 x, u32 y, u32 c, bool ofm) { u32 element_size, storage = fm->precision >> 14; int tile = 0; @@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, switch ((fm->precision >> 6) & 0x3) { // format case 0: //nhwc: - addr += x * fm->stride_x + c; + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); + addr += x * fm->stride_x + c * element_size; break; case 1: //nhcwb16: - element_size = BIT((fm->precision >> 1) & 0x3); + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size; break; @@ -268,7 +269,7 @@ static int calc_sizes(struct drm_device *ddev, return -EINVAL; len = feat_matrix_length(info, &st->ifm, ifm_width, - ifm_height, st->ifm.depth); + ifm_height, st->ifm.depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -277,7 +278,7 @@ static int calc_sizes(struct drm_device *ddev, if (ifm2) { len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, - 0, st->ofm.depth); + 0, st->ofm.depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) @@ -309,7 +310,7 @@ static int calc_sizes(struct drm_device *ddev, } len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) @@ -333,7 +334,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; len = feat_matrix_length(info, &st->ifm, width, - height, depth); + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -346,7 +347,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; len = feat_matrix_length(info, &st->ifm2, width, - height, depth); + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) @@ -354,7 +355,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, } len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) -- cgit v1.3.1 From 6b7e0066294d23ad1fd37f4326c32e8090fb8b65 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 20 Jul 2026 18:14:48 -0500 Subject: accel: ethosu: Handle U85 internal chaining buffer The Ethos-U85 supports an internal chaining buffer as temporary storage between some operations. When chaining is activated, the IFM/OFM region setting selects a chaining buffer rather than a region, and the IFM/OFM base addresses don't matter. In this case, the feature matrix size calculations should be skipped. Otherwise, the command stream will be intermittently rejected depending on prior feature matrix base addresses. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Acked-by: Tomeu Vizoso Link: https://patch.msgid.link/20260720231450.485221-2-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 1daff32610c7..d50fed64d4d9 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, return len; } -static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, +static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm) +{ + u32 storage = fm->precision >> 14; + + return !ethosu_is_u65(edev) && storage == 2; +} + +static u64 feat_matrix_length(struct ethosu_device *edev, + struct ethosu_validated_cmdstream_info *info, struct feat_matrix *fm, u32 x, u32 y, u32 c, bool ofm) { @@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, if (fm->region < 0) return U64_MAX; + if (feat_matrix_chained(edev, fm)) + return 0; + switch (storage) { case 0: if (x >= fm->width0 + 1) { @@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, tile = 1; } break; + default: + return U64_MAX; } if (fm->base[tile] == U64_MAX) return U64_MAX; @@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2, bool weight, bool scale) { + struct ethosu_device *edev = to_ethosu_device(ddev); u64 len; if (ifm) { @@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev, if (ifm_height < 0 || ifm_width < 0) return -EINVAL; - len = feat_matrix_length(info, &st->ifm, ifm_width, + len = feat_matrix_length(edev, info, &st->ifm, ifm_width, ifm_height, st->ifm.depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); @@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev, } if (ifm2) { - len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, + len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth, 0, st->ofm.depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); @@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev, st->scale[0].base + st->scale[0].length); } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } @@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2) { + struct ethosu_device *edev = to_ethosu_device(ddev); u32 height, width, depth; u64 len; @@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm, width, + len = feat_matrix_length(edev, info, &st->ifm, width, height, depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); @@ -346,7 +362,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm2, width, + len = feat_matrix_length(edev, info, &st->ifm2, width, height, depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); @@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev, return -EINVAL; } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } -- cgit v1.3.1 From 4c92afb4c143526d340545ca581e88e6952ea511 Mon Sep 17 00:00:00 2001 From: Satyanarayana K V P Date: Tue, 21 Jul 2026 10:52:14 +0530 Subject: drm/xe/vf: Add drm_dev guards when detaching CCS read/write buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CCS read/write buffers are freed during BO destruction. In some cases, BOs may be destroyed after the device is unbound but while the DRM structure remains valid, leading to NULL pointer dereferences when accessing device resources. BUG: kernel NULL pointer dereference, address: 0000000000000000 PGD 0 P4D 0 Oops: Oops: 0000 [#1] SMP NOPTI CPU: 0 UID: 0 PID: 9376 Comm: xe_pat Not tainted 7.2.0-rc2+ #1 PREEMPT(lazy) RIP: 0010:xe_sriov_vf_ccs_rw_update_bb_addr+0x4d/0xa0 [xe] RSP: 0018:ffffcf304110b9c8 EFLAGS: 00010246 RAX: ffff8a85c38a0a00 RBX: 00000000810ef000 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8a85c39c1888 RBP: ffffcf304110b9e8 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8a85c39c1888 R13: 0000000000000000 R14: ffff8a85c39b4f28 R15: ffff8a85c3885000 FS: 0000000000000000(0000) GS:ffff8a878b809000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000010314a002 CR4: 0000000000772ef0 PKRU: 55555554 Call Trace: xe_migrate_ccs_rw_copy_clear+0x98/0x120 [xe] xe_sriov_vf_ccs_detach_bo+0x2c/0x60 [xe] xe_ttm_bo_delete_mem_notify+0xc8/0xe0 [xe] ttm_bo_cleanup_memtype_use+0x26/0x80 [ttm] ttm_bo_release+0x29e/0x2d0 [ttm] ttm_bo_fini+0x39/0x70 [ttm] xe_gem_object_free+0x1f/0x30 [xe] drm_gem_object_free+0x1d/0x40 ttm_bo_vm_close+0x5f/0x90 [ttm] remove_vma+0x2c/0x70 tear_down_vmas+0x63/0xf0 exit_mmap+0x20d/0x3f0 __mmput+0x45/0x170 mmput+0x31/0x40 do_exit+0x2ba/0xac0 do_group_exit+0x2d/0xb0 __x64_sys_exit_group+0x18/0x20 x64_sys_call+0x14a0/0x2390 do_syscall_64+0xdd/0x640 ? count_memcg_events+0xea/0x240 ? handle_mm_fault+0x1ec/0x2f0 Fixes: 864690cf4dd6 ("drm/xe/vf: Attach and detach CCS copy commands with BO") Signed-off-by: Satyanarayana K V P Cc: Matthew Brost Cc: Michal Wajdeczko Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260721052215.2267228-2-satyanarayana.k.v.p@intel.com (cherry picked from commit 1ae415a6eefe5004954a1d352b1718faca8844ef) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_migrate.c | 16 ++++++++++------ drivers/gpu/drm/xe/xe_migrate.h | 3 ++- drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 7d28290e7d1c..be787f331768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -1313,6 +1313,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * content. * @src_bo: The buffer object @src is currently bound to. * @read_write : Creates BB commands for CCS read/write. + * @bound: Device is bound * * Directly clearing the BB lacks atomicity and can lead to undefined * behavior if the vCPU is halted mid-operation during the clearing @@ -1325,7 +1326,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * Returns: None. */ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write) + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound) { struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write]; struct xe_device *xe = xe_bo_device(src_bo); @@ -1339,13 +1341,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, bb_pool = ctx->mem.ccs_bb_pool; scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) { - xe_mem_pool_swap_shadow_locked(bb_pool); + if (bound) { + xe_mem_pool_swap_shadow_locked(bb_pool); - cs = xe_mem_pool_node_cpu_addr(bb); - memset(cs, MI_NOOP, bb->sa_node.size); - xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + cs = xe_mem_pool_node_cpu_addr(bb); + memset(cs, MI_NOOP, bb->sa_node.size); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); - xe_mem_pool_sync_shadow_locked(bb); + xe_mem_pool_sync_shadow_locked(bb); + } xe_mem_pool_free_node(bb); src_bo->bb_ccs[read_write] = NULL; } diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 78e5b63f3ebe..c3a268b01768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -142,7 +142,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, enum xe_sriov_vf_ccs_rw_ctxs read_write); void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write); + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound); struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate); struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate); diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 6787564629c6..a8c831fbee3b 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -3,6 +3,8 @@ * Copyright © 2025 Intel Corporation */ +#include + #include "instructions/xe_mi_commands.h" #include "instructions/xe_gpu_commands.h" #include "xe_bb.h" @@ -446,7 +448,7 @@ err_unwind: */ for_each_ccs_rw_ctx(ctx_id) { if (bo->bb_ccs[ctx_id]) - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true); } return err; } @@ -466,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; struct xe_mem_pool_node *bb; + bool bound; + int idx; xe_assert(xe, IS_VF_CCS_READY(xe)); if (!xe_bo_has_valid_ccs_bb(bo)) return 0; + bound = drm_dev_enter(&xe->drm, &idx); + for_each_ccs_rw_ctx(ctx_id) { bb = bo->bb_ccs[ctx_id]; if (!bb) continue; - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound); } + + if (bound) + drm_dev_exit(idx); + return 0; } -- cgit v1.3.1 From 1f1be4ba643fb67410d753111c37ae42a2e2a814 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 21 Jul 2026 13:43:53 -0700 Subject: drm/pagemap: Clear driver-provided PFNs from migration PFN array DRM pagemap overloads the migration PFN array to store driver-provided PFNs before calling migrate_vma_*() to finalize the migration. If an error occurs during the incremental copy phase, the migration PFN entries are reverted to their original state. After reverting the device-folio mutations, clear any remaining driver-provided PFNs to avoid confusing the migrate_vma_*() helpers. Also clear any driver-provided PFNs if populate_devmem_pfn() fails, as a precaution against stale entries being interpreted as migration PFNs. Reported-by: Sashiko Fixes: 3902846af36b ("drm/pagemap Fix error paths in drm_pagemap_migrate_to_devmem") Fixes: ec265e1f1cfc ("drm/pagemap: Support source migration over interconnect") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260721204353.1082632-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_pagemap.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 15c78eca180b..892b325fa99b 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -727,8 +727,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, } err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst); - if (err) - goto err_aborted_migration; + if (err) { + npages = 0; + goto err_finalize; + } own_pages = 0; @@ -807,8 +809,11 @@ next: msecs_to_jiffies(mdetails->timeslice_ms); err_finalize: - if (err) + if (err) { drm_pagemap_migration_unlock_put_pages(npages, migrate.dst); + for (i = npages; i < npages_in_range(start, end); ++i) + migrate.dst[i] = 0; + } err_aborted_migration: migrate_vma_pages(&migrate); -- cgit v1.3.1 From 04b177544a040cbafab760d6b766381c6b22e0a8 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 21 Jul 2026 15:32:44 -0700 Subject: drm/pagemap: Guard HPAGE_PMD_ORDER use with CONFIG_ARCH_ENABLE_THP_MIGRATION HPAGE_PMD_SHIFT expands to BUILD_BUG() when CONFIG_PGTABLE_HAS_HUGE_LEAVES is not set, causing a compile error when both CONFIG_TRANSPARENT_HUGEPAGE and CONFIG_HUGETLB_PAGE are disabled: drivers/gpu/drm/drm_pagemap.c:480:12: error: call to '__compiletime_assert_458' declared with 'error' attribute: BUILD_BUG failed 480 | order = HPAGE_PMD_ORDER; | ^ include/linux/huge_mm.h:117:26: note: expanded from macro 'HPAGE_PMD_ORDER' 117 | #define HPAGE_PMD_ORDER (HPAGE_PMD_SHIFT-PAGE_SHIFT) | ^ include/linux/huge_mm.h:113:28: note: expanded from macro 'HPAGE_PMD_SHIFT' 113 | #define HPAGE_PMD_SHIFT ({ BUILD_BUG(); 0; }) Define DRM_PAGEMAP_PMD_ORDER, which maps to HPAGE_PMD_ORDER when CONFIG_ARCH_ENABLE_THP_MIGRATION is enabled and to -1 otherwise. This is safe because all code paths that use DRM_PAGEMAP_PMD_ORDER are reachable only when CONFIG_ARCH_ENABLE_THP_MIGRATION is enabled. Fixes: 139ab31aea8a ("drm/pagemap: Correct cpages calculation for migrate_vma_setup") Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202607201914.LpAGsbXs-lkp@intel.com/ Cc: Jan Stancek Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Tested-by: Jan Stancek Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260721223244.1102276-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_pagemap.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 892b325fa99b..4a794544b7dc 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -12,6 +12,12 @@ #include #include +#if IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION) +#define DRM_PAGEMAP_PMD_ORDER HPAGE_PMD_ORDER +#else +#define DRM_PAGEMAP_PMD_ORDER (-1) +#endif + /** * DOC: Overview * @@ -579,7 +585,7 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages) order = folio_order(folio); cpages += NR_PAGES(order); } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) { - order = HPAGE_PMD_ORDER; + order = DRM_PAGEMAP_PMD_ORDER; cpages += NR_PAGES(order); } @@ -767,10 +773,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, if (migrate.src[i] & MIGRATE_PFN_COMPOUND) { drm_WARN_ONCE(dpagemap->drm, src_page && - folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER, + folio_order(page_folio(src_page)) != + DRM_PAGEMAP_PMD_ORDER, "Unexpected folio order\n"); - order = HPAGE_PMD_ORDER; + order = DRM_PAGEMAP_PMD_ORDER; migrate.dst[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < NR_PAGES(order) && i + j < npages; j++) -- cgit v1.3.1 From 7a6004c230a7931569666a235e99d443b2319b66 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 17 Jul 2026 08:14:30 -0300 Subject: mailmap: Update Maíra Canal's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My university email will cease to exist in the next few days, so map it to a personal email address. Reviewed-by: André Almeida Acked-by: Maíra Canal Link: https://patch.msgid.link/20260717111559.2759217-1-maira.canal@usp.br Signed-off-by: Maíra Canal --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 12f3acdebd72..e8fa40478028 100644 --- a/.mailmap +++ b/.mailmap @@ -539,6 +539,7 @@ Maciej W. Rozycki Maciej W. Rozycki Maharaja Kennadyrajan Maheshwar Ajja +Maíra Canal Malathi Gottam Manikanta Pubbisetty Manivannan Sadhasivam -- cgit v1.3.1 From f7df2da0d1c375c1c4c70e1e0b569592de0d6b73 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sat, 18 Jul 2026 10:44:36 -0300 Subject: drm/v3d: Reach the GMP through the hub registers on V3D 7.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_idle_axi() drains the GPU's memory interface for a safe powerdown by using the V3D_GMP_CFG register. It reached both registers with the macros V3D_CORE_READ and V3D_CORE_WRITE. On V3D 7.x the GMP is no longer a per-core block; it lives in the hub register region. Reaching it through the per-core register block addresses the wrong region. Select the hub accessors (V3D_{READ,WRITE}) for the GMP on V3D 7.x and keep the per-core path for earlier generations. Cc: stable@vger.kernel.org Fixes: 0ad5bc1ce463 ("drm/v3d: fix up register addresses for V3D 7.x") Link: https://patch.msgid.link/20260718-v3d-pm-axi-transactions-v1-1-4ecd7729ed70@igalia.com Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_gem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index c43d9af41374..bb0eec870794 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -39,6 +39,18 @@ v3d_init_core(struct v3d_dev *v3d, int core) static void v3d_idle_axi(struct v3d_dev *v3d, int core) { + if (v3d->ver >= V3D_GEN_71) { + V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); + + if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) & + (V3D_GMP_STATUS_RD_COUNT_MASK | + V3D_GMP_STATUS_WR_COUNT_MASK | + V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { + drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n"); + } + return; + } + V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) & -- cgit v1.3.1 From 07e769ba3d82ac614725c81c512a8b54d33f2c9e Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sat, 18 Jul 2026 10:44:37 -0300 Subject: drm/v3d: Idle AXI transactions before disabling the clock on suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, v3d_power_suspend() removes the GPU clock without first quiescing the GPU's memory interface (AXI). If the clock is cut while the core still has outstanding AXI transactions in flight, the hardware is frozen mid-transaction. That corrupted state survives the power cycle, and the first job submitted after the next resume will cause a GPU hang accompanied by an L2T "pte invalid" MMU fault. The hardware already provides a safe-powerdown sequence for this: request the GMP to stop and wait for outstanding reads/writes to drain (v3d_idle_axi()), plus the GCA safe shutdown on pre-4.1 HW (v3d_idle_gca()). The driver implements both, but the runtime PM support added later never invoked them when powering the GPU down. Perform the safe-powerdown sequence in v3d_power_suspend() before disabling the clock, while the core is still powered. Link: https://github.com/raspberrypi/linux/issues/7443 Link: https://github.com/raspberrypi/linux/issues/7488 Fixes: 458f2a712ab4 ("drm/v3d: Introduce Runtime Power Management") Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260718-v3d-pm-axi-transactions-v1-2-4ecd7729ed70@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_drv.h | 2 ++ drivers/gpu/drm/v3d/v3d_gem.c | 4 ++-- drivers/gpu/drm/v3d/v3d_power.c | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 4ebe175a8c6b..423bcfe331a0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -571,6 +571,8 @@ extern bool super_pages; void v3d_init_hw_state(struct v3d_dev *v3d); int v3d_gem_init(struct drm_device *dev); void v3d_gem_destroy(struct drm_device *dev); +void v3d_idle_axi(struct v3d_dev *v3d, int core); +void v3d_idle_gca(struct v3d_dev *v3d); void v3d_reset_sms(struct v3d_dev *v3d); void v3d_reset(struct v3d_dev *v3d); void v3d_invalidate_caches(struct v3d_dev *v3d); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index bb0eec870794..23a0db9575a5 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -36,7 +36,7 @@ v3d_init_core(struct v3d_dev *v3d, int core) V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0); } -static void +void v3d_idle_axi(struct v3d_dev *v3d, int core) { if (v3d->ver >= V3D_GEN_71) { @@ -61,7 +61,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core) } } -static void +void v3d_idle_gca(struct v3d_dev *v3d) { if (v3d->ver >= V3D_GEN_41) diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c index f7df6393d38f..c53146316079 100644 --- a/drivers/gpu/drm/v3d/v3d_power.c +++ b/drivers/gpu/drm/v3d/v3d_power.c @@ -54,8 +54,15 @@ int v3d_power_suspend(struct device *dev) v3d_clean_caches(v3d); + /* Wait until V3D has no active or pending AXI transactions. */ + v3d_idle_axi(v3d, 0); + v3d_idle_gca(v3d); + ret = v3d_suspend_sms(v3d); if (ret) { + /* Staying active: undo the GMP STOP_REQ from v3d_idle_axi(). */ + V3D_WRITE(V3D_GMP_CFG(v3d->ver), + V3D_READ(V3D_GMP_CFG(v3d->ver)) & ~V3D_GMP_CFG_STOP_REQ); v3d_irq_enable(v3d); return ret; } -- cgit v1.3.1 From fa989f2cc351aec3643350489553c9bdfb3adf9f Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 13 Jul 2026 10:22:13 +0200 Subject: drm/panel: ilitek-ili9881c: do not fail probe if iovcc is absent Commit 4c95b2b7d49e ("drm/panel: ilitek-ili9881c: support Waveshare 7.0" DSI panel") adds an additional iovcc regulator that other Ilitek ili9881c based panels apparently do not have or need. The commit goes out of its way to make usage of this new regulator optional, dutifully testing if the field in `struct ili9881c` is NULL before touching the new regulator. However, in the probe function, it unconditionally fails if devm_regulator_get_optional returns an error. devm_regulator_get_optional() returns -ENODEV if the regulator is missing, causing probe to fail for other panels that do not have an iovcc-supply set in the device tree. Fixes: 4c95b2b7d49e ("drm/panel: ilitek-ili9881c: support Waveshare 7.0" DSI panel") Signed-off-by: David Oberhollenzer Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260713082213.75759-1-david.oberhollenzer@sigma-star.at --- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0652cdb57d11..6c8d02712566 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -2562,9 +2562,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) "Couldn't get our power regulator\n"); ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc"); - if (IS_ERR(ctx->iovcc)) - return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), + if (IS_ERR(ctx->iovcc)) { + if (PTR_ERR(ctx->iovcc) != -ENODEV) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), "Couldn't get our iovcc regulator\n"); + ctx->iovcc = NULL; + } ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) -- cgit v1.3.1 From fc03f930bd9fd7c09617e41b1743e50ba659707c Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 01:26:32 +0100 Subject: drm/panel: ilitek-ili9882t: fix unmet dependency for DRM_PANEL_ILITEK_ILI9882T Currently, DRM_PANEL_ILITEK_ILI9882T selects DRM_DISPLAY_DSC_HELPER without ensuring DRM_DISPLAY_HELPER is also enabled, causing an unmet dependency and build failure. Other similar options select DRM_DISPLAY_HELPER, let's do the same here. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: 68e28facbc8a ("drm/panel: ilitek-ili9882t: Select DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260712002632.2323484-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7450b27622a2..613f09ccc7cb 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -332,6 +332,7 @@ config DRM_PANEL_ILITEK_ILI9882T depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y if you want to enable support for panels based on the -- cgit v1.3.1 From 3667bc164849fee4f1b18b182bdfe643f758ca17 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 01:15:14 +0100 Subject: drm/panel: s6e3ha8: fix unmet dependency on DRM_DISPLAY_HELPER Currently, DRM_PANEL_SAMSUNG_S6E3HA8 selects DRM_DISPLAY_DSC_HELPER without ensuring its dependency, DRM_DISPLAY_HELPER, is enabled, causing an unmet dependency. Let's select DRM_DISPLAY_HELPER as other similar options do. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: fd3b2c5f40a1 ("drm/panel: s6e3ha8: select CONFIG_DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260712001514.2318597-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 613f09ccc7cb..fda7a3ee67e6 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -955,6 +955,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y or M here if you want to enable support for the -- cgit v1.3.1 From aaa5be0258db1709e254b4150af5c2fa5518c548 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 00:42:30 +0100 Subject: drm/panel: fix unmet dependency bug for DRM_PANEL_HIMAX_HX83121A Currently, DRM_PANEL_HIMAX_HX83121A selects DRM_DISPLAY_DSC_HELPER without also ensuring DRM_DISPLAY_HELPER is enabled, causing an unmet dependency: WARNING: unmet direct dependencies detected for DRM_DISPLAY_DSC_HELPER Depends on [n]: HAS_IOMEM [=y] && DRM [=m] && DRM_DISPLAY_HELPER [=n] Selected by [m]: - DRM_PANEL_HIMAX_HX83121A [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m] - DRM_PANEL_ILITEK_ILI9882T [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m] Many other DRM_PANEL_* options select DRM_DISPLAY_HELPER when selecting DRM_DISPLAY_DSC_HELPER, let's do the same here. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: defab7b01e08 ("drm/panel: hx83121a: select DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260711234230.2236041-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index fda7a3ee67e6..3eac4d0f88bc 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -232,6 +232,7 @@ config DRM_PANEL_HIMAX_HX83121A depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER select DRM_KMS_HELPER help -- cgit v1.3.1 From 7dc3680b7ffe01add3e9299fde8471d2dd53a8ae Mon Sep 17 00:00:00 2001 From: Gregor Herburger Date: Tue, 21 Jul 2026 17:38:19 +0200 Subject: drm/vc4: hvs/v3d: Fix null dereference in unbind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hvs and v3d drivers use dev_get_drvdata(master) in their unbind functions. Since the vc4-drm gets removed before its dependent drivers (vc4_hvs/vc4_v3d) the vc4_hvs_unbind/vc4_v3d_unbind functions try to get drvdata of its master and fails with a null dereference error. Use the data pointer passed to the unbind functions directly instead of dev_get_drvdata(master). This avoids using potentially freed memory. Fixes: d3f5168a0810 ("drm/vc4: Bind and initialize the V3D engine.") Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.") Signed-off-by: Gregor Herburger Link: https://patch.msgid.link/20260721-rpi-vc4-fix-v2-1-b813dcd01dc7@linutronix.de Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_hvs.c | 2 +- drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 184d51ea3fa5..e715147d091c 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) static void vc4_hvs_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_hvs *hvs = vc4->hvs; struct drm_mm_node *node, *next; diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index d31b906cb8e7..f32410420d3e 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -494,7 +494,7 @@ err_put_runtime_pm: static void vc4_v3d_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); vc4_irq_uninstall(drm); -- cgit v1.3.1 From 9b5ce5c496efd20c1c662cedba88465d39ec1f93 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 08:32:40 +0800 Subject: drm/gma500: return errors from Oaktrail HDMI I2C reads xfer_read() waits for the HDMI I2C transaction to reach I2C_TRANSACTION_DONE, but it ignores both timeout and signal returns from wait_for_completion_interruptible_timeout(). If the interrupt never advances the transaction state, the loop can wait forever. Return -ETIMEDOUT when the completion wait expires, propagate interrupted waits, and make the I2C master_xfer callback return the first transfer error instead of reporting a successful message count. Signed-off-by: Pengpeng Hou Signed-off-by: Patrik Jakobsson Link: https://patch.msgid.link/20260625003240.6923-1-pengpeng@iscas.ac.cn --- drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c index 2a7916ca5907..a780643f25aa 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -98,6 +98,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; u32 temp; + int ret; i2c_dev->status = I2C_STAT_INIT; i2c_dev->msg = pmsg; @@ -109,9 +110,14 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) HDMI_WRITE(HDMI_HI2CHCR, temp); HDMI_READ(HDMI_HI2CHCR); - while (i2c_dev->status != I2C_TRANSACTION_DONE) - wait_for_completion_interruptible_timeout(&i2c_dev->complete, + while (i2c_dev->status != I2C_TRANSACTION_DONE) { + ret = wait_for_completion_interruptible_timeout(&i2c_dev->complete, 10 * HZ); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + } return 0; } @@ -130,7 +136,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, { struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - int i; + int i, ret = 0; mutex_lock(&i2c_dev->i2c_lock); @@ -142,9 +148,11 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, for (i = 0; i < num; i++) { if (pmsg->len && pmsg->buf) { if (pmsg->flags & I2C_M_RD) - xfer_read(adap, pmsg); + ret = xfer_read(adap, pmsg); else - xfer_write(adap, pmsg); + ret = xfer_write(adap, pmsg); + if (ret) + break; } pmsg++; /* next message */ } @@ -154,6 +162,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, mutex_unlock(&i2c_dev->i2c_lock); + if (ret) + return ret; + return i; } -- cgit v1.3.1 From 5d8ed6b64220ad629aade5f174e3f690c37435f9 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 21 Jul 2026 17:04:38 +0530 Subject: drm/xe/i2c: Allow per domain unique id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI bus, device and function can be same for devices existing across different domains. Allow per domain unique identifier while registering platform device to prevent name conflict. Fixes: f0e53aadd702 ("drm/xe: Support for I2C attached MCUs") Signed-off-by: Raag Jadav Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260721113438.651100-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit a79f6abc8b516b5bd906e2eca8121e3549ee163f) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 706783863d07..f05f23221c1b 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c) struct platform_device *pdev; struct fwnode_handle *fwnode; int ret; + u32 id; fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL); if (IS_ERR(fwnode)) return PTR_ERR(fwnode); + id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci); + /* * Not using platform_device_register_full() here because we don't have * a handle to the platform_device before it returns. xe_i2c_notifier() * uses that handle, but it may be called before * platform_device_register_full() is done. */ - pdev = platform_device_alloc(adapter_name, pci_dev_id(pci)); + pdev = platform_device_alloc(adapter_name, id); if (!pdev) { ret = -ENOMEM; goto err_fwnode_remove; -- cgit v1.3.1 From d2c6800ad1802bed72a6de1416536737f114f1d6 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 21 Jul 2026 20:55:14 +0000 Subject: drm/xe/vm: Fix SVM leak on resv obj alloc failure in xe_vm_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") made xe_svm_init() unconditional in xe_vm_create() and extended it to also initialize a "simple" gpusvm state for non-fault-mode VMs. The matching xe_svm_fini() call in xe_vm_close_and_put() was updated to run unconditionally, but the error unwind path in xe_vm_create() was not. On the drm_gpuvm_resv_object_alloc() failure path, xe_svm_init() has already succeeded but xe_svm_fini() is only called when XE_VM_FLAG_FAULT_MODE is set. For non-fault-mode VMs this leaves vm->svm.gpusvm partially initialized and leaks the resources allocated by drm_gpusvm_init(). For fault-mode VMs, xe_svm_init() additionally acquires the pagemap owner via drm_pagemap_acquire_owner() and the pagemaps via xe_svm_get_pagemaps(). Those resources are released by xe_svm_close(), not xe_svm_fini(). On the same error path, xe_svm_close() is not called either, so fault-mode VMs leak the pagemap owner and pagemaps. Fix both leaks: - Call xe_svm_fini() unconditionally on the err_svm_fini path, matching the unconditional xe_svm_init() call. Move the vm->size = 0 assignment out of the conditional so the xe_vm_is_closed() assert in xe_svm_fini() (and xe_svm_close()) holds for both modes. - Call xe_svm_close() for fault-mode VMs before xe_svm_fini(), matching the ordering used in xe_vm_close_and_put(). Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Cc: Matthew Auld Assisted-by: Claude:claude-opus-4.7 Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260721205516.4058959-2-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit ca2a3587d577ba764e0fe628fb676244fc33ddd4) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 32ded13491ca..67819deb45e3 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1809,10 +1809,10 @@ err_close: return ERR_PTR(err); err_svm_fini: - if (flags & XE_VM_FLAG_FAULT_MODE) { - vm->size = 0; /* close the vm */ - xe_svm_fini(vm); - } + vm->size = 0; /* close the vm */ + if (flags & XE_VM_FLAG_FAULT_MODE) + xe_svm_close(vm); + xe_svm_fini(vm); err_no_resv: mutex_destroy(&vm->snap_mutex); for_each_tile(tile, xe, id) -- cgit v1.3.1 From b04a248cfa6cfa1e7dc9ce91cb1eb88b1a70dd69 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Fri, 3 Jul 2026 17:07:43 +0200 Subject: drm/tests: shmem: Set DMA mask to 64-bit in drm_gem_shmem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_gem_shmem_test_purge [1] and drm_gem_shmem_test_get_pages_sgt [2] intermittently fail on ppc64le and s390x CI systems with a DMA address overflow: DMA addr 0x0000000100307000+4096 overflow (mask ffffffff, bus limit 0) WARNING: kernel/dma/direct.h:114 dma_direct_map_sg+0x778/0x920 drm_gem_shmem_test_purge: ASSERTION FAILED at drivers/gpu/drm/tests/drm_gem_shmem_test.c:330 Expected sgt is not error, but is: -5 The call chain leading to the failure is: drm_gem_shmem_test_purge() / drm_gem_shmem_test_get_pages_sgt() drm_gem_shmem_get_pages_sgt() drm_gem_shmem_get_pages_sgt_locked() [drm_gem_shmem_helper.c] dma_map_sgtable() [mapping.c] __dma_map_sg_attrs() dma_direct_map_sg() [direct.c] dma_direct_map_phys() [kernel/dma/direct.h] dma_capable() Checks addr against DMA mask -> FAILS: addr > 0xFFFFFFFF The root cause is that KUnit devices are initialized with a 32-bit DMA mask (DMA_BIT_MASK(32)) in lib/kunit/device.c. On ppc64le and s390x systems with physical memory above 4GB, page allocations can land at addresses that exceed this mask. When drm_gem_shmem_get_pages_sgt() attempts to DMA-map these pages via dma_map_sgtable(), the DMA layer rejects the mapping because the physical address overflows the 32-bit mask. The failure is intermittent because pages may or may not be allocated above 4GB on any given run depend on memory pressure. Fix by setting a 64-bit DMA mask on the device before calling drm_gem_shmem_get_pages_sgt() for all tests, following the same pattern already used in drm_gem_shmem_test_obj_create_private(). [1] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_s390x/15128551935/artifacts/jobwatch/logs/recipes/21561049/tasks/220716793/results/1014626315/logs/dmesg.log [2] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_ppc64le/15128551933/artifacts/jobwatch/logs/recipes/21561041/tasks/220716705/results/1014628163/logs/dmesg.log Fixes: 93032ae634d4 ("drm/test: add a test suite for GEM objects backed by shmem") Closes: https://datawarehouse.cki-project.org/issue/5345 Closes: https://datawarehouse.cki-project.org/issue/3184 Assisted-by: Claude:claude-4.6-opus Reviewed-by: Thomas Zimmermann Signed-off-by: José Expósito Link: https://patch.msgid.link/20260703150808.3832-1-jose.exposito89@gmail.com --- drivers/gpu/drm/tests/drm_gem_shmem_test.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c index 44a190109249..5e69ff1d1ad6 100644 --- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c +++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c @@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test) sg_init_one(sgt->sgl, buf, TEST_SIZE); /* - * Set the DMA mask to 64-bits and map the sgtables - * otherwise drm_gem_shmem_free will cause a warning - * on debug kernels. + * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on + * debug kernels. */ - ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64)); - KUNIT_ASSERT_EQ(test, ret, 0); - ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test) { struct device *dev; struct drm_device *drm_dev; + int ret; /* Allocate a parent device */ dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + /* + * Set the DMA mask to 64-bits to avoid intermittent failures calling + * drm_gem_shmem_get_pages_sgt(). + */ + ret = dma_set_mask(dev, DMA_BIT_MASK(64)); + KUNIT_ASSERT_EQ(test, ret, 0); + /* * The DRM core will automatically initialize the GEM core and create * a DRM Memory Manager object which provides an address space pool -- cgit v1.3.1 From 6273dd3ffb54ec581855b82ae77331b66028249c Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 20 Jul 2026 16:44:26 +0800 Subject: drm/vc4: Shut down BO cache timer before teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BO cache timer callback schedules time_work, and time_work can rearm the timer through vc4_bo_cache_free_old(). vc4_bo_cache_destroy() deletes the timer and then cancels the work, which does not break that cycle: the work being cancelled can rearm the timer, and the timer then queues work again after teardown. Use timer_shutdown_sync() instead, so the timer cannot be rearmed and the cycle ends with cancel_work_sync(). Fixes: c826a6e10644 ("drm/vc4: Add a BO cache.") Cc: stable@vger.kernel.org Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260720084426.1632508-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_bo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 5e7c46dd7823..49ea2ed0996b 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -1048,7 +1048,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) struct vc4_dev *vc4 = to_vc4_dev(dev); int i; - timer_delete(&vc4->bo_cache.time_timer); + timer_shutdown_sync(&vc4->bo_cache.time_timer); cancel_work_sync(&vc4->bo_cache.time_work); vc4_bo_cache_purge(dev); -- cgit v1.3.1 From 1ff399c4cd132a24c73e5e237a11cb9d6b68dff1 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 24 Jul 2026 08:02:14 +0200 Subject: Revert "drm/pagemap: Guard HPAGE_PMD_ORDER use with CONFIG_ARCH_ENABLE_THP_MIGRATION" This reverts commit 04b177544a040cbafab760d6b766381c6b22e0a8. The original author requested it to be reverted, as it conflicts with changes in the -next branch for MM: "I'm not sure who is doing the drm-misc-fixes PR, but if you are can you omit this patch: https://patchwork.freedesktop.org/series/170865/ I guess this conflicts with MM changes in their next tree and it easy enough on our side to do this slightly differently to avoid a conflict so going to post revert + a different change. If this is already sent nbd." Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/drm_pagemap.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 4a794544b7dc..892b325fa99b 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -12,12 +12,6 @@ #include #include -#if IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION) -#define DRM_PAGEMAP_PMD_ORDER HPAGE_PMD_ORDER -#else -#define DRM_PAGEMAP_PMD_ORDER (-1) -#endif - /** * DOC: Overview * @@ -585,7 +579,7 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages) order = folio_order(folio); cpages += NR_PAGES(order); } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) { - order = DRM_PAGEMAP_PMD_ORDER; + order = HPAGE_PMD_ORDER; cpages += NR_PAGES(order); } @@ -773,11 +767,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, if (migrate.src[i] & MIGRATE_PFN_COMPOUND) { drm_WARN_ONCE(dpagemap->drm, src_page && - folio_order(page_folio(src_page)) != - DRM_PAGEMAP_PMD_ORDER, + folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER, "Unexpected folio order\n"); - order = DRM_PAGEMAP_PMD_ORDER; + order = HPAGE_PMD_ORDER; migrate.dst[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < NR_PAGES(order) && i + j < npages; j++) -- cgit v1.3.1 From 82730dba0cf9d9524af0ceeb7eb6b5c3ab1bdb87 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 14:01:59 -0400 Subject: drm/amd/display: Fix flip-done timeouts on mode1 reset The vblank on/off callbacks mixed use of amdgpu_irq_get/put() and amdgpu_dm_crtc_set_vupdate_irq() to enable and disable IRQs. With get/put, base driver will callback into DC to disable IRQs when refcount == 0. With set_vupdate_irq(), DC is called directly to disable IRQs, bypassing base driver's refcount tracking. During gpu reset, base driver can restore IRQs via amdgpu_irq_gpu_reset_resume_helper() > amdgpu_irq_update(). So if get/put() is not used (i.e. refcount == 0), then vupdate_irq will be disabled. This is problematic if DRM requests vblank on before amdgpu_irq_update() is called: drm_vblank_on() > set_vupdate_irq() enables vupdate_irq, but the refcount is still 0. gpu_reset_resume_helper() > irq_update() then immediately disables it, thus leading to flip done timeouts. This is made worse on DCN since VUPDATE_NO_LOCK is the only IRQ enabled. Prior to 8382cd234981, a combination of GRPH_FLIP and VSTARTUP IRQs were used, and they used get/put(). This explains why 8382cd234981 exposed this issue. Fix by using get/put() instead of set_vupdate_irq(). DCE is unchanged, since it relies on unbalanced enable/disable calls based on VRR status, and hence requires direct set_vupdate_irq(). Plus, it also uses GRPH_FLIP and VLINE IRQs, which are properly tracked by get/put(). Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723180159.52121-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index b43cd68cde67..95d3da3c4199 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -280,10 +280,19 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) * is enabled. On DCE, vupdate is only needed in VRR mode. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { - rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + if (enable) { + rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc); + } } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { - /* vblank irq on -> Only need vupdate irq in vrr mode */ + /* vblank irq on -> Only need vupdate irq in vrr mode + * Not ref-counted since we need explicit enable/disable + * for DCE VRR handling + */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); } else { -- cgit v1.3.1 From fbbaca9e208733652828ea98d00f09f260a7770e Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 09:44:50 -0400 Subject: drm/amd/display: Fix missing DCE check in dm_gpureset_toggle_interrupts() This line was lost when cping from amd-staging-drm-next to drm-fixes. So add it back. Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Reported-by: Lu Yao Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723134450.13838-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9c564cd5edee..eaf19ec843e8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3324,7 +3324,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, acrtc = get_crtc_by_otg_inst( adev, state->stream_status[i].primary_otg_inst); - if (acrtc && state->stream_status[i].plane_count != 0) { + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; if (rc) -- cgit v1.3.1