Replace the open-coded shmem-backup loop in ttm_backup_backup_folio() with a thin wrapper around the new shmem_backup_folio() helper. The wrapper maps the helper's (int errno, *nr_pages_backed) contract onto TTM's existing (s64 handle) return convention: - 0 -> base handle - -ENOMEM with *nr_pages_backed > 0 -> base handle (short backup; partial progress kept) - any other error -> that error verbatim The mid-compound -ENOMEM fault-injection point that used to live inside the per-subpage loop moves up to the wrapper and takes a new shape: instead of synthesizing an error partway through the loop, the wrapper passes order-1 to shmem_backup_folio() when ttm_backup_fault_inject_folio() fires. shmem_backup_folio() then backs up the first half of the compound normally, returns 0 with *nr_pages_backed = (1 << (order - 1)), and TTM's caller sees the same "short backup, nr_backed < (1 << order)" it would see on a real mid-compound OOM and drives its reactive-split path unchanged. Injection is skipped for order == 0 since there is nothing to shrink; the previous "inject only on i > 0" guard mapped onto the same observable subset of runs. No functional change intended for non-injection paths. Cc: Hugh Dickins Cc: Baolin Wang Cc: Andrew Morton 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-mm@kvack.org Cc: linux-kernel@vger.kernel.org Suggested-by: Christoph Hellwig Signed-off-by: Matthew Brost Assisted-by: GitHub-Copilot:claude-sonnet-5 --- The patch is based on drm-tip rather than the core MM branches to facilitate Intel CI testing and initial review. It can be rebased onto the core MM branches in a subsequent revision. --- drivers/gpu/drm/ttm/ttm_backup.c | 91 ++++++++------------------------ 1 file changed, 21 insertions(+), 70 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c index 3c067aadc52d..a6c4e6cb643b 100644 --- a/drivers/gpu/drm/ttm/ttm_backup.c +++ b/drivers/gpu/drm/ttm/ttm_backup.c @@ -105,76 +105,27 @@ ttm_backup_backup_folio(struct file *backup, struct folio *folio, gfp_t folio_gfp, gfp_t alloc_gfp, pgoff_t *nr_pages_backed) { - struct address_space *mapping = backup->f_mapping; - int nr_pages = 1 << order; - struct folio *to_folio; - int ret, i; - - *nr_pages_backed = 0; - - for (i = 0; i < nr_pages; ) { - int to_nr, j; - - /* - * 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 (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); - } - - folio_put(to_folio); - i += to_nr; - *nr_pages_backed = i; - } - - return ttm_backup_shmem_idx_to_handle(idx); + unsigned int backup_order = order; + int err; + + /* + * Fault injection: back up only the first half of the folio to + * simulate a mid-compound OOM. The caller sees *nr_pages_backed + * < (1 << order) on success and drives its reactive-split path + * exactly as it would on a real short return. order == 0 cannot + * be shrunk further, so injection is skipped in that case. + */ + if (IS_ENABLED(CONFIG_FAULT_INJECTION) && order && + ttm_backup_fault_inject_folio()) + backup_order = order - 1; + + err = shmem_backup_folio(folio, backup, idx, alloc_gfp, backup_order, + nr_pages_backed, writeback); + + if (!err || (err == -ENOMEM && *nr_pages_backed)) + return ttm_backup_shmem_idx_to_handle(idx); + + return err; } /** -- 2.34.1