linux/drivers/gpu/drm/ttm
Matthew Brost a3fdf74ffa drm/ttm/pool: back up at native page order
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 <christian.koenig@amd.com>
Cc: Huang Rui <ray.huang@amd.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org
Fixes: b63d715b80 ("drm/ttm/pool, drm/ttm/tt: Provide a helper to shrink pages")
Suggested-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Link: https://patch.msgid.link/20260716201358.4086085-1-matthew.brost@intel.com
2026-07-17 09:09:27 -07:00
..
tests drm/ttm/tests: Remove checks from ttm_pool_free_no_dma_alloc 2026-04-10 06:54:49 +10:00
Makefile drm/ttm: Provide a shmem backup implementation 2025-03-05 17:08:59 +01:00
ttm_agp_backend.c Convert 'alloc_obj' family to use the new default GFP_KERNEL argument 2026-02-21 17:09:51 -08:00
ttm_backup.c drm/ttm/pool: back up at native page order 2026-07-17 09:09:27 -07:00
ttm_bo_internal.h drm/ttm: rename ttm_bo_put to _fini v3 2025-09-17 14:03:21 +02:00
ttm_bo_util.c drm/ttm: Fix ttm_bo_shrink() infinite LRU walk on backup failure 2026-05-14 15:32:29 +02:00
ttm_bo_vm.c drm-misc-next for 6.19: 2025-12-26 18:15:33 +10:00
ttm_bo.c drm/ttm: Convert -EAGAIN from dmem_cgroup_try_charge to -ENOSPC 2026-05-14 13:12:57 +02:00
ttm_device.c drm/ttm: Tidy ttm_operation_ctx initialization 2025-12-03 14:48:41 +00:00
ttm_execbuf_util.c drm/ttm: Include <linux/export.h> 2025-06-16 09:02:44 +02:00
ttm_module.c drm/ttm: add pgprot handling for RISC-V 2025-10-28 09:19:46 +01:00
ttm_module.h
ttm_pool_internal.h drm/ttm/pool: back up at native page order 2026-07-17 09:09:27 -07:00
ttm_pool.c drm/ttm/pool: back up at native page order 2026-07-17 09:09:27 -07:00
ttm_range_manager.c Convert 'alloc_flex' family to use the new default GFP_KERNEL argument 2026-02-21 17:09:51 -08:00
ttm_resource.c drm/ttm: Convert -EAGAIN from dmem_cgroup_try_charge to -ENOSPC 2026-05-14 13:12:57 +02:00
ttm_sys_manager.c Convert 'alloc_obj' family to use the new default GFP_KERNEL argument 2026-02-21 17:09:51 -08:00
ttm_tt.c Convert more 'alloc_obj' cases to default GFP_KERNEL arguments 2026-02-21 20:03:00 -08:00