All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Thomas Hellström" <thomas.hellstrom@linux.intel.com>
To: intel-xe@lists.freedesktop.org
Cc: "Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"David Hildenbrand" <david@kernel.org>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Liam R. Howlett" <liam@infradead.org>,
	"Vlastimil Babka" <vbabka@kernel.org>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Michal Hocko" <mhocko@suse.com>,
	"Hugh Dickins" <hughd@google.com>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Brendan Jackman" <jackmanb@google.com>,
	"Johannes Weiner" <hannes@cmpxchg.org>, "Zi Yan" <ziy@nvidia.com>,
	"Christian Koenig" <christian.koenig@amd.com>,
	"Huang Rui" <ray.huang@amd.com>,
	"Matthew Auld" <matthew.auld@intel.com>,
	"Matthew Brost" <matthew.brost@intel.com>,
	"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
	"Maxime Ripard" <mripard@kernel.org>,
	"Thomas Zimmermann" <tzimmermann@suse.de>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	dri-devel@lists.freedesktop.org, linux-mm@kvack.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] drm/ttm: Use ttm_backup_insert_folio() for zero-copy swapout
Date: Tue, 12 May 2026 13:03:39 +0200	[thread overview]
Message-ID: <20260512110339.6244-3-thomas.hellstrom@linux.intel.com> (raw)
In-Reply-To: <20260512110339.6244-1-thomas.hellstrom@linux.intel.com>

Add ttm_backup_insert_folio(), a thin wrapper around shmem_insert_folio()
that returns a handle, for use by drivers with large isolated folios.

Replace the alloc+copy ttm_backup_backup_page() path in ttm_pool_backup()
with the zero-copy ttm_backup_insert_folio() path.

On success NR_GPU_ACTIVE is decremented and the caller's reference is
released; shmem takes ownership.  The alloc_gfp argument used for
allocating shmem backing pages is no longer needed.

If insertion fails for a higher-order page, it is split into order-0
pages with ttm_pool_split_for_swap() and the loop retries each page
individually.

Assisted-by: GitHub_Copilot:claude-sonnet-4.6
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
---
 drivers/gpu/drm/ttm/ttm_backup.c | 92 ++++++++++++--------------------
 drivers/gpu/drm/ttm/ttm_pool.c   | 67 ++++++++++++++++-------
 include/drm/ttm/ttm_backup.h     | 11 ++--
 3 files changed, 87 insertions(+), 83 deletions(-)

diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c
index 81df4cb5606b..a37e9404b895 100644
--- a/drivers/gpu/drm/ttm/ttm_backup.c
+++ b/drivers/gpu/drm/ttm/ttm_backup.c
@@ -6,7 +6,6 @@
 #include <drm/ttm/ttm_backup.h>
 
 #include <linux/export.h>
-#include <linux/page-flags.h>
 #include <linux/swap.h>
 
 /*
@@ -68,73 +67,50 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
 }
 
 /**
- * ttm_backup_backup_page() - Backup a page
+ * ttm_backup_insert_folio() - Zero-copy insert of an isolated folio into backup.
  * @backup: The struct backup pointer to use.
- * @page: The page to back up.
- * @writeback: Whether to perform immediate writeback of the page.
- * This may have performance implications.
- * @idx: A unique integer for each page 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.
- * @alloc_gfp: The gfp to be used when allocating memory.
+ * @folio: The folio to insert. Must be isolated (not on LRU), unlocked,
+ *         have exactly one reference (the caller's), and have no page-table
+ *         mappings.  The folio must not be swapbacked or in the swapcache,
+ *         and folio->private must have been cleared by the caller.
+ * @order: The allocation order of @folio.  If @order > 0 and @folio is not
+ *         already a large folio, it is promoted to a compound folio of this
+ *         order (see shmem_insert_folio()).  split_page() must NOT have been
+ *         called; tail-page refcounts must be 0.
+ * @writeback: Whether to attempt immediate writeback to swap after insertion.
+ *             Best-effort; failure is silently ignored.
+ * @idx: Page-cache index within @backup.  Must be aligned to (1 << @order).
+ * @folio_gfp: The gfp value used when the folio was allocated.
+ *             Used for memory-cgroup charging.
  *
- * Context: If called from reclaim context, the caller needs to
- * assert that the shrinker gfp has __GFP_FS set, to avoid
- * deadlocking on lock_page(). If @writeback is set to true and
- * called from reclaim context, the caller also needs to assert
- * that the shrinker gfp has __GFP_IO set, since without it,
- * we're not allowed to start backup IO.
+ * Context: May be called from reclaim context.  If @writeback is true, the
+ * caller must assert that the shrinker gfp has __GFP_IO set.
  *
- * Return: A handle on success. Negative error code on failure.
+ * The folio is transferred zero-copy into the shmem page cache.  On success
+ * the caller should release their reference with folio_put() and track the
+ * handle for later recovery via ttm_backup_copy_page() and release via
+ * ttm_backup_drop().  Handles for sub-pages of a compound folio follow
+ * sequentially: handle + j addresses sub-page j.
  *
- * 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 positive handle on success. Negative error code on failure;
+ *         the folio is returned to its original non-compound state and the
+ *         caller retains ownership.
  */
 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_insert_folio(struct file *backup, struct folio *folio,
+			unsigned int order, bool writeback, pgoff_t idx,
+			gfp_t folio_gfp)
 {
-	struct address_space *mapping = backup->f_mapping;
-	unsigned long handle = 0;
-	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);
-		/*
-		 * If writeout succeeds, it unlocks the folio.	errors
-		 * are otherwise dropped, since writeout is only best
-		 * effort here.
-		 */
-		if (ret)
-			folio_unlock(to_folio);
-	} else {
-		folio_unlock(to_folio);
-	}
-
-	folio_put(to_folio);
-
-	return handle;
+	WARN_ON_ONCE(folio_get_private(folio));
+	ret = shmem_insert_folio(backup, folio, order, idx, writeback, folio_gfp);
+	if (ret)
+		return ret;
+
+	return ttm_backup_shmem_idx_to_handle(idx);
 }
+EXPORT_SYMBOL_GPL(ttm_backup_insert_folio);
 
 /**
  * ttm_backup_fini() - Free the struct backup resources after last use.
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index d380a3c7fe40..8ea3a125c465 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -487,7 +487,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_insert_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.
@@ -1045,12 +1045,11 @@ 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, num_pages, npages;
+	unsigned long j;
 
 	if (WARN_ON(ttm_tt_is_backed_up(tt)))
 		return -EINVAL;
@@ -1070,7 +1069,8 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
 			unsigned int order;
 
 			page = tt->pages[i];
-			if (unlikely(!page)) {
+			if (unlikely(!page ||
+				     ttm_backup_page_ptr_is_handle(page))) {
 				num_pages = 1;
 				continue;
 			}
@@ -1098,34 +1098,63 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
 	else
 		gfp = GFP_HIGHUSER;
 
-	alloc_gfp = GFP_KERNEL | __GFP_HIGH | __GFP_NOWARN | __GFP_RETRY_MAYFAIL;
-
 	num_pages = tt->num_pages;
 
 	/* Pretend doing fault injection by shrinking only half of the pages. */
 	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;
 
-		ttm_pool_split_for_swap(pool, page);
+		/* Already-handled entry from a previous attempt. */
+		if (unlikely(ttm_backup_page_ptr_is_handle(page)))
+			continue;
+
+		order = ttm_pool_page_order(pool, page);
+		npages = 1UL << order;
 
-		shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
-						 gfp, alloc_gfp);
-		if (shandle < 0) {
-			/* We allow partially shrunken tts */
-			ret = shandle;
+		/*
+		 * If fault injection truncated num_pages mid-compound, skip
+		 * the partial tail rather than inserting it.
+		 */
+		if (unlikely(i + npages > num_pages))
+			break;
+
+		/*
+		 * Transfer this page zero-copy into shmem.  page->private
+		 * stores the TTM order; clear it before inserting.
+		 */
+		page->private = 0;
+		handle = ttm_backup_insert_folio(backup, page_folio(page),
+						 order, flags->writeback,
+						 i, gfp);
+		if (unlikely(handle < 0)) {
+			if (order) {
+				page->private = order;
+				ttm_pool_split_for_swap(pool, page);
+				npages = 0;
+				continue;
+			}
+			ret = (int)handle;
 			break;
 		}
-		handle = shandle;
-		tt->pages[i] = ttm_backup_handle_to_page_ptr(handle);
-		__free_pages_gpu_account(page, 0, false);
-		shrunken++;
+
+		/*
+		 * NR_GPU_ACTIVE is node-only; use mod_node_page_state()
+		 * directly after the folio becomes memcg-charged.
+		 */
+		mod_node_page_state(page_pgdat(page), NR_GPU_ACTIVE, -(1 << order));
+		folio_put(page_folio(page));
+		for (j = 0; j < npages; j++)
+			tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j);
+		shrunken += npages;
 	}
 
 	return shrunken ? shrunken : ret;
diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h
index 29b9c855af77..0c2feed0bffb 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 ttm_backup_insert_folio()
+ * function to an (invalid) struct page pointer suitable for a struct page array.
  *
  * Return: An (invalid) struct page pointer.
  */
@@ -59,9 +58,9 @@ 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_insert_folio(struct file *backup, struct folio *folio,
+			unsigned int order, bool writeback, pgoff_t idx,
+			gfp_t folio_gfp);
 
 void ttm_backup_fini(struct file *backup);
 
-- 
2.54.0


  parent reply	other threads:[~2026-05-12 11:04 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-12 11:03 [PATCH 0/2] Insert instead of copy pages into shmem when shrinking Thomas Hellström
2026-05-12 11:03 ` [PATCH 1/2] mm/shmem: add shmem_insert_folio() Thomas Hellström
2026-05-12 11:07   ` David Hildenbrand (Arm)
2026-05-12 11:31     ` Thomas Hellström
2026-05-12 20:03       ` David Hildenbrand (Arm)
2026-05-13  7:47         ` Christian König
2026-05-13  8:31           ` Thomas Hellström
2026-05-13  9:30             ` David Hildenbrand (Arm)
2026-05-13  8:37           ` David Hildenbrand (Arm)
2026-05-13  8:51             ` Thomas Hellström
2026-05-13 10:03               ` David Hildenbrand (Arm)
2026-05-13 10:37                 ` Thomas Hellström
2026-05-13 11:36                   ` David Hildenbrand (Arm)
2026-05-13 14:53                     ` Thomas Hellström
2026-05-13 19:35                       ` David Hildenbrand (Arm)
2026-05-14 10:40                         ` Thomas Hellström
2026-05-13 11:54             ` Christian König
2026-05-13 19:43               ` David Hildenbrand (Arm)
2026-05-12 11:03 ` Thomas Hellström [this message]
2026-05-12 16:46 ` ✗ CI.checkpatch: warning for Insert instead of copy pages into shmem when shrinking Patchwork
2026-05-12 16:47 ` ✓ CI.KUnit: success " Patchwork
2026-05-12 18:11 ` ✓ Xe.CI.BAT: " Patchwork
2026-05-13  7:30 ` ✗ Xe.CI.FULL: failure " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260512110339.6244-3-thomas.hellstrom@linux.intel.com \
    --to=thomas.hellstrom@linux.intel.com \
    --cc=airlied@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=christian.koenig@amd.com \
    --cc=david@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=hannes@cmpxchg.org \
    --cc=hughd@google.com \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=jackmanb@google.com \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=matthew.auld@intel.com \
    --cc=matthew.brost@intel.com \
    --cc=mhocko@suse.com \
    --cc=mripard@kernel.org \
    --cc=ray.huang@amd.com \
    --cc=rppt@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=surenb@google.com \
    --cc=tzimmermann@suse.de \
    --cc=vbabka@kernel.org \
    --cc=ziy@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.