Re: [PATCH v14 3/8] drm/ttm/pool: Provide a helper to shrink pages

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am 03.12.24 um 14:42 schrieb Thomas Hellström:
On Tue, 2024-12-03 at 14:12 +0100, Christian König wrote:
Am 15.11.24 um 16:01 schrieb Thomas Hellström:
Provide a helper to shrink ttm_tt page-vectors on a per-page
basis. A ttm_backup backend could then in theory get away with
allocating a single temporary page for each struct ttm_tt.

This is accomplished by splitting larger pages before trying to
back them up.

In the future we could allow ttm_backup to handle backing up
large pages as well, but currently there's no benefit in
doing that, since the shmem backup backend would have to
split those anyway to avoid allocating too much temporary
memory, and if the backend instead inserts pages into the
swap-cache, those are split on reclaim by the core.

Due to potential backup- and recover errors, allow partially
swapped
out struct ttm_tt's, although mark them as swapped out stopping
them
from being swapped out a second time. More details in the
ttm_pool.c
DOC section.

v2:
- A couple of cleanups and error fixes in ttm_pool_back_up_tt.
- s/back_up/backup/
- Add a writeback parameter to the exported interface.
v8:
- Use a struct for flags for readability (Matt Brost)
- Address misc other review comments (Matt Brost)
v9:
- Update the kerneldoc for the ttm_tt::backup field.
v10:
- Rebase.
v13:
- Rebase on ttm_backup interface change. Update kerneldoc.
- Rebase and adjust ttm_tt_is_swapped().

Cc: Christian König <christian.koenig@xxxxxxx>
Cc: Somalapuram Amaranath <Amaranath.Somalapuram@xxxxxxx>
Cc: Matthew Brost <matthew.brost@xxxxxxxxx>
Cc: <dri-devel@xxxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Thomas Hellström <thomas.hellstrom@xxxxxxxxxxxxxxx>
Reviewed-by: Matthew Brost <matthew.brost@xxxxxxxxx>
---
  drivers/gpu/drm/ttm/ttm_pool.c | 396
+++++++++++++++++++++++++++++++--
  drivers/gpu/drm/ttm/ttm_tt.c   |  37 +++
  include/drm/ttm/ttm_pool.h     |   6 +
  include/drm/ttm/ttm_tt.h       |  32 ++-
  4 files changed, 457 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/ttm/ttm_pool.c
b/drivers/gpu/drm/ttm/ttm_pool.c
index 8504dbe19c1a..f58864439edb 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -41,6 +41,7 @@
  #include <asm/set_memory.h>
  #endif
  
+#include <drm/ttm/ttm_backup.h>
  #include <drm/ttm/ttm_pool.h>
  #include <drm/ttm/ttm_tt.h>
  #include <drm/ttm/ttm_bo.h>
@@ -58,6 +59,32 @@ struct ttm_pool_dma {
  	unsigned long vaddr;
  };
  
+/**
+ * struct ttm_pool_tt_restore - State representing restore from
backup
+ * @alloced_pages: Total number of already allocated pages for the
ttm_tt.
+ * @restored_pages: Number of (sub) pages restored from swap for
this
+ *		     chunk of 1 << @order pages.
+ * @first_page: The ttm page ptr representing for @old_pages[0].
+ * @caching_divide: Page pointer where subsequent pages are
cached.
+ * @old_pages: Backup copy of page pointers that were replaced by
the new
+ *	       page allocation.
+ * @pool: The pool used for page allocation while restoring.
+ * @order: The order of the last page allocated while restoring.
+ *
+ * Recovery from backup might fail when we've recovered less than
the
+ * full ttm_tt. In order not to loose any data (yet), keep
information
+ * around that allows us to restart a failed ttm backup recovery.
+ */
+struct ttm_pool_tt_restore {
+	pgoff_t alloced_pages;
+	pgoff_t restored_pages;
+	struct page **first_page;
+	struct page **caching_divide;
+	struct ttm_pool *pool;
+	unsigned int order;
+	struct page *old_pages[];
+};
+
  static unsigned long page_pool_size;
  
  MODULE_PARM_DESC(page_pool_size, "Number of pages in the
WC/UC/DMA pool");
@@ -354,11 +381,105 @@ static unsigned int
ttm_pool_page_order(struct ttm_pool *pool, struct page *p)
  	return p->private;
  }
  
+/*
+ * To be able to insert single pages into backup directly,
+ * we need to split multi-order page allocations and make them
look
+ * like single-page allocations.
+ */
+static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct
page *p)
+{
+	unsigned int order = ttm_pool_page_order(pool, p);
+	pgoff_t nr;
+
+	if (!order)
+		return;
+
+	split_page(p, order);
What exactly should split_page() do here and why is that necessary?

IIRC that function just updated the reference count and updated
things 
like page owner tracking and memcg accounting. Which should both be 
completely irrelevant here.

Or do you just do that so that you can free each page individually?
Yes, exactly. Like For a 2MiB page we'd otherwise have to allocate 2MiB
of shmem backing storage, potentially from kernel reserves before we
could actually free anything. Since (currently) the shmem objects we
use are 4K-page only, this should make the process "allocate shmem and
back up" much less likely to deplete the kernel memory reserves.

Ah, yes that makes totally sense now.


Taking a step back and looking at potentially other solution, like
direct insertion into the swap cache, then even if inserting a 2MiB
page into the swap cache, vmscan would split it before writeback, and
still it didn't appear very stable. So inserting one 4K page at a time
seemed neccessary. If I were to take a guess that's why shmem, when
configured for 2MiB pages, like with i915, also splits the pages before
moving to swap-cache / writeback.



        
+	nr = 1UL << order;
+	while (nr--)
+		(p++)->private = 0;
+}
+
+/**
+ * DOC: Partial backup and restoration of a struct ttm_tt.
+ *
+ * Swapout using ttm_backup_backup_page() 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.
+ *
+ * Backup failure is easily handled by using a ttm_tt pages vector
that holds
+ * both swap entries and page pointers. This has to be taken into
account when
+ * restoring such a ttm_tt from backup, and when freeing it while
backed up.
+ * When restoring, for simplicity, new pages are actually
allocated from the
+ * pool and the contents of any old pages are copied in and then
the old pages
+ * are released.
+ *
+ * For restoration failures, the struct ttm_pool_tt_restore holds
sufficient state
+ * to be able to resume an interrupted restore, and that structure
is freed once
+ * the restoration is complete. If the struct ttm_tt is destroyed
while there
+ * is a valid struct ttm_pool_tt_restore attached, that is also
properly taken
+ * care of.
+ */
+
+static bool ttm_pool_restore_valid(const struct
ttm_pool_tt_restore *restore)
+{
+	return restore && restore->restored_pages < (1 << restore-
order);
+}
+
+static int ttm_pool_restore_tt(struct ttm_pool_tt_restore
*restore,
+			       struct ttm_backup *backup,
+			       struct ttm_operation_ctx *ctx)
+{
+	unsigned int i, nr = 1 << restore->order;
+	int ret = 0;
+
+	if (!ttm_pool_restore_valid(restore))
+		return 0;
+
+	for (i = restore->restored_pages; i < nr; ++i) {
+		struct page *p = restore->old_pages[i];
+
+		if (ttm_backup_page_ptr_is_handle(p)) {
+			unsigned long handle =
ttm_backup_page_ptr_to_handle(p);
+
+			if (handle == 0)
+				continue;
+
+			ret = ttm_backup_copy_page
+				(backup, restore->first_page[i],
+				 handle, ctx->interruptible);
That coding style looks really odd, I didn't even notice that it is a
function call initially.

Maybe put everything under the if into a separate function.
At a minimum, I'll fix up the formatting here.


        
+			if (ret)
+				break;
+
+			ttm_backup_drop(backup, handle);
+		} else if (p) {
+			/*
+			 * We could probably avoid splitting the
old page
+			 * using clever logic, but ATM we don't
care, as
+			 * we prioritize releasing memory ASAP.
Note that
+			 * here, the old retained page is always
write-back
+			 * cached.
+			 */
+			ttm_pool_split_for_swap(restore->pool, p);
+			copy_highpage(restore->first_page[i], p);
+			__free_pages(p, 0);
+		}
+
+		restore->restored_pages++;
+		restore->old_pages[i] = NULL;
+		cond_resched();
There is a push to remove cond_resched(), see here: 
https://patchwork.kernel.org/project/linux-mm/patch/20231107230822.371443-30-ankur.a.arora@xxxxxxxxxx/

Not sure in which discussion that removal went, but IIRC we should
not 
add any new users of it.
I'll read up on that and remove if needed. I'm curious how / if
voluntary preemption is going to be handled.

I didn't fully understood it either, but the push kind of seems to be that drivers or in this cases subsystems are not supposed to mess with cond_resched() any more and just rely on preemptive kernels.


        
+	}
+
+	return ret;
+}
+
  /* Called when we got a page, either from a pool or newly
allocated */
  static int ttm_pool_page_allocated(struct ttm_pool *pool,
unsigned int order,
  				   struct page *p, dma_addr_t
**dma_addr,
  				   unsigned long *num_pages,
-				   struct page ***pages)
+				   struct page ***pages,
+				   struct ttm_pool_tt_restore
*restore)
  {
  	unsigned int i;
  	int r;
@@ -369,6 +490,16 @@ static int ttm_pool_page_allocated(struct
ttm_pool *pool, unsigned int order,
  			return r;
  	}
  
+	if (restore) {
+		memcpy(restore->old_pages, *pages,
+		       (1 << order) * sizeof(*restore-
old_pages));
+		memset(*pages, 0, (1 << order) * sizeof(**pages));
+		restore->order = order;
+		restore->restored_pages = 0;
+		restore->first_page = *pages;
+		restore->alloced_pages += 1UL << order;
+	}
+
  	*num_pages -= 1 << order;
  	for (i = 1 << order; i; --i, ++(*pages), ++p)
  		**pages = p;
@@ -394,22 +525,39 @@ static void ttm_pool_free_range(struct
ttm_pool *pool, struct ttm_tt *tt,
  				pgoff_t start_page, pgoff_t
end_page)
  {
  	struct page **pages = &tt->pages[start_page];
+	struct ttm_backup *backup = tt->backup;
  	unsigned int order;
  	pgoff_t i, nr;
  
  	for (i = start_page; i < end_page; i += nr, pages += nr) {
  		struct ttm_pool_type *pt = NULL;
+		struct page *p = *pages;
+
+		if (ttm_backup_page_ptr_is_handle(p)) {
+			unsigned long handle =
ttm_backup_page_ptr_to_handle(p);
+
+			nr = 1;
+			if (handle != 0)
+				ttm_backup_drop(backup, handle);
+			continue;
+		}
+
+		if (pool) {
+			order = ttm_pool_page_order(pool, p);
+			nr = (1UL << order);
+			if (tt->dma_address)
+				ttm_pool_unmap(pool, tt-
dma_address[i], nr);
  
-		order = ttm_pool_page_order(pool, *pages);
-		nr = (1UL << order);
-		if (tt->dma_address)
-			ttm_pool_unmap(pool, tt->dma_address[i],
nr);
+			pt = ttm_pool_select_type(pool, caching,
order);
+		} else {
+			order = p->private;
+			nr = (1UL << order);
+		}
  
-		pt = ttm_pool_select_type(pool, caching, order);
  		if (pt)
-			ttm_pool_type_give(pt, *pages);
+			ttm_pool_type_give(pt, p);
  		else
-			ttm_pool_free_page(pool, caching, order,
*pages);
+			ttm_pool_free_page(pool, caching, order,
p);
  	}
  }
  
@@ -453,9 +601,36 @@ int ttm_pool_alloc(struct ttm_pool *pool,
struct ttm_tt *tt,
  	else
  		gfp_flags |= GFP_HIGHUSER;
  
-	for (order = min_t(unsigned int, MAX_PAGE_ORDER,
__fls(num_pages));
-	     num_pages;
-	     order = min_t(unsigned int, order, __fls(num_pages)))
{
+	order = min_t(unsigned int, MAX_PAGE_ORDER,
__fls(num_pages));
+
+	if (tt->page_flags & TTM_TT_FLAG_PRIV_BACKED_UP) {
+		if (!tt->restore) {
+			gfp_t gfp = GFP_KERNEL | __GFP_NOWARN;
+
+			if (ctx->gfp_retry_mayfail)
+				gfp |= __GFP_RETRY_MAYFAIL;
+
+			tt->restore =
+				kvzalloc(struct_size(tt->restore,
old_pages,
+						     (size_t)1 <<
order), gfp);
+			if (!tt->restore)
+				return -ENOMEM;
+		} else if (ttm_pool_restore_valid(tt->restore)) {
+			struct ttm_pool_tt_restore *restore = tt-
restore;
+
+			num_pages -= restore->alloced_pages;
+			order = min_t(unsigned int, order,
__fls(num_pages));
+			pages += restore->alloced_pages;
+			r = ttm_pool_restore_tt(restore, tt-
backup, ctx);
+			if (r)
+				return r;
+			caching = restore->caching_divide;
+		}
+
+		tt->restore->pool = pool;
+	}
Hui? Why is that part of the allocation function now?

At bare minimum I would expect that this is a new function.
It's because we now have partially backed up tts, so the restore is
interleaved on a per-page basis, replacing the backup handles with
page-pointers. I'll see if I can separate out at least the
initialization here.

Yeah, that kind of makes sense.

My expectation was just that we now have explicit ttm_pool_swapout() and ttm_pool_swapin() functions.

Christian.


/Thomas


Regards,
Christian.

+
+	for (; num_pages; order = min_t(unsigned int, order,
__fls(num_pages))) {
  		struct ttm_pool_type *pt;
  
  		page_caching = tt->caching;
@@ -472,11 +647,19 @@ int ttm_pool_alloc(struct ttm_pool *pool,
struct ttm_tt *tt,
  				r = ttm_pool_page_allocated(pool,
order, p,
  							   
&dma_addr,
  							   
&num_pages,
-							   
&pages);
+							   
&pages,
+							    tt-
restore);
  				if (r)
  					goto error_free_page;
  
  				caching = pages;
+				if (ttm_pool_restore_valid(tt-
restore)) {
+					r =
ttm_pool_restore_tt(tt->restore, tt->backup,
+								ct
x);
+					if (r)
+						goto
error_free_all;
+				}
+
  				if (num_pages < (1 << order))
  					break;
  
@@ -496,9 +679,17 @@ int ttm_pool_alloc(struct ttm_pool *pool,
struct ttm_tt *tt,
  				caching = pages;
  			}
  			r = ttm_pool_page_allocated(pool, order,
p, &dma_addr,
-						    &num_pages,
&pages);
+						    &num_pages,
&pages,
+						    tt->restore);
  			if (r)
  				goto error_free_page;
+
+			if (ttm_pool_restore_valid(tt->restore)) {
+				r = ttm_pool_restore_tt(tt-
restore, tt->backup, ctx);
+				if (r)
+					goto error_free_all;
+			}
+
  			if (PageHighMem(p))
  				caching = pages;
  		}
@@ -517,12 +708,26 @@ int ttm_pool_alloc(struct ttm_pool *pool,
struct ttm_tt *tt,
  	if (r)
  		goto error_free_all;
  
+	if (tt->restore) {
+		kvfree(tt->restore);
+		tt->restore = NULL;
+	}
+
+	if (tt->page_flags & TTM_TT_FLAG_PRIV_BACKED_UP)
+		tt->page_flags &= ~(TTM_TT_FLAG_PRIV_BACKED_UP |
+				    TTM_TT_FLAG_SWAPPED);
+
  	return 0;
  
  error_free_page:
  	ttm_pool_free_page(pool, page_caching, order, p);
  
  error_free_all:
+	if (tt->page_flags & TTM_TT_FLAG_PRIV_BACKED_UP) {
+		tt->restore->caching_divide = caching;
+		return r;
+	}
+
  	num_pages = tt->num_pages - num_pages;
  	caching_divide = caching - tt->pages;
  	ttm_pool_free_range(pool, tt, tt->caching, 0,
caching_divide);
@@ -549,6 +754,171 @@ void ttm_pool_free(struct ttm_pool *pool,
struct ttm_tt *tt)
  }
  EXPORT_SYMBOL(ttm_pool_free);
  
+/**
+ * ttm_pool_release_backed_up() - Release content of a swapped-out
struct ttm_tt
+ * @tt: The struct ttm_tt.
+ *
+ * Release handles with associated content or any remaining pages
of
+ * a backed-up struct ttm_tt.
+ */
+void ttm_pool_release_backed_up(struct ttm_tt *tt)
+{
+	struct ttm_backup *backup = tt->backup;
+	struct ttm_pool_tt_restore *restore;
+	pgoff_t i, start_page = 0;
+	unsigned long handle;
+
+	if (!(tt->page_flags & TTM_TT_FLAG_PRIV_BACKED_UP))
+		return;
+
+	restore = tt->restore;
+
+	if (ttm_pool_restore_valid(restore)) {
+		pgoff_t nr = 1UL << restore->order;
+
+		for (i = restore->restored_pages; i < nr; ++i) {
+			struct page *p = restore->old_pages[i];
+
+			if (ttm_backup_page_ptr_is_handle(p)) {
+				handle =
ttm_backup_page_ptr_to_handle(p);
+				if (handle == 0)
+					continue;
+
+				ttm_backup_drop(backup, handle);
+			} else if (p) {
+				ttm_pool_split_for_swap(restore-
pool, p);
+				__free_pages(p, 0);
+			}
+		}
+	}
+
+	if (restore) {
+		pgoff_t mid = restore->caching_divide - tt->pages;
+
+		start_page = restore->alloced_pages;
+		/* Pages that might be dma-mapped and non-cached
*/
+		ttm_pool_free_range(restore->pool, tt, tt-
caching,
+				    0, mid);
+		/* Pages that might be dma-mapped but cached */
+		ttm_pool_free_range(restore->pool, tt, ttm_cached,
+				    mid, restore->alloced_pages);
+	}
+
+	/* Shrunken pages. Cached and not dma-mapped. */
+	ttm_pool_free_range(NULL, tt, ttm_cached, start_page, tt-
num_pages);
+
+	if (restore) {
+		kvfree(restore);
+		tt->restore = NULL;
+	}
+
+	tt->page_flags &= ~(TTM_TT_FLAG_PRIV_BACKED_UP |
TTM_TT_FLAG_SWAPPED);
+}
+
+/**
+ * ttm_pool_backup_tt() - Back up or purge a struct ttm_tt
+ * @pool: The pool used when allocating the struct ttm_tt.
+ * @ttm: The struct ttm_tt.
+ * @flags: Flags to govern the backup behaviour.
+ *
+ * Back up or purge a struct ttm_tt. If @purge is true, then
+ * all pages will be freed directly to the system rather than to
the pool
+ * they were allocated from, making the function behave similarly
to
+ * ttm_pool_free(). If @purge is false the pages will be backed up
instead,
+ * exchanged for handles.
+ * A subsequent call to ttm_pool_alloc() will then read back the
content and
+ * a subsequent call to ttm_pool_release_shrunken() will drop it.
+ * If backup of a page fails for whatever reason, @ttm will still
be
+ * partially backed up, retaining those pages for which backup
fails.
+ *
+ * Return: Number of pages actually backed up or freed, or
negative
+ * error code on error.
+ */
+long ttm_pool_backup_tt(struct ttm_pool *pool, struct ttm_tt *ttm,
+			const struct ttm_backup_flags *flags)
+{
+	struct ttm_backup *backup = ttm->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;
+
+	if ((!ttm_backup_bytes_avail() && !flags->purge) ||
+	    pool->use_dma_alloc ||
+	    (ttm->page_flags & TTM_TT_FLAG_PRIV_BACKED_UP))
+		return -EBUSY;
+
+#ifdef CONFIG_X86
+	/* Anything returned to the system needs to be cached. */
+	if (ttm->caching != ttm_cached)
+		set_pages_array_wb(ttm->pages, ttm->num_pages);
+#endif
+
+	if (ttm->dma_address || flags->purge) {
+		for (i = 0; i < ttm->num_pages; i += num_pages) {
+			unsigned int order;
+
+			page = ttm->pages[i];
+			if (unlikely(!page)) {
+				num_pages = 1;
+				continue;
+			}
+
+			order = ttm_pool_page_order(pool, page);
+			num_pages = 1UL << order;
+			if (ttm->dma_address)
+				ttm_pool_unmap(pool, ttm-
dma_address[i],
+					       num_pages);
+			if (flags->purge) {
+				shrunken += num_pages;
+				page->private = 0;
+				__free_pages(page, order);
+				memset(ttm->pages + i, 0,
+				       num_pages * sizeof(*ttm-
pages));
+			}
+		}
+	}
+
+	if (flags->purge)
+		return shrunken;
+
+	if (pool->use_dma32)
+		gfp = GFP_DMA32;
+	else
+		gfp = GFP_HIGHUSER;
+
+	alloc_gfp = GFP_KERNEL | __GFP_HIGH | __GFP_NOWARN |
__GFP_RETRY_MAYFAIL;
+
+	for (i = 0; i < ttm->num_pages; ++i) {
+		page = ttm->pages[i];
+		if (unlikely(!page))
+			continue;
+
+		ttm_pool_split_for_swap(pool, page);
+
+		handle = ttm_backup_backup_page(backup, page,
flags->writeback, i,
+						gfp, alloc_gfp);
+		if (handle) {
+			ttm->pages[i] =
ttm_backup_handle_to_page_ptr(handle);
+			put_page(page);
+			shrunken++;
+		} else {
+			/* We allow partially shrunken tts */
+			ret = -ENOMEM;
+			break;
+		}
+	}
+
+	if (shrunken)
+		ttm->page_flags |= (TTM_TT_FLAG_PRIV_BACKED_UP |
+				    TTM_TT_FLAG_SWAPPED);
+
+	return shrunken ? shrunken : ret;
+}
+
  /**
   * ttm_pool_init - Initialize a pool
   *
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c
b/drivers/gpu/drm/ttm/ttm_tt.c
index 3baf215eca23..dd4eabe4ad79 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -40,6 +40,7 @@
  #include <drm/drm_cache.h>
  #include <drm/drm_device.h>
  #include <drm/drm_util.h>
+#include <drm/ttm/ttm_backup.h>
  #include <drm/ttm/ttm_bo.h>
  #include <drm/ttm/ttm_tt.h>
  
@@ -158,6 +159,8 @@ static void ttm_tt_init_fields(struct ttm_tt
*ttm,
  	ttm->swap_storage = NULL;
  	ttm->sg = bo->sg;
  	ttm->caching = caching;
+	ttm->restore = NULL;
+	ttm->backup = NULL;
  }
  
  int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo,
@@ -182,6 +185,12 @@ void ttm_tt_fini(struct ttm_tt *ttm)
  		fput(ttm->swap_storage);
  	ttm->swap_storage = NULL;
  
+	ttm_pool_release_backed_up(ttm);
+	if (ttm->backup) {
+		ttm_backup_fini(ttm->backup);
+		ttm->backup = NULL;
+	}
+
  	if (ttm->pages)
  		kvfree(ttm->pages);
  	else
@@ -253,6 +262,34 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
  }
  EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_tt_swapin);
  
+/**
+ * ttm_tt_backup() - Helper to back up a struct ttm_tt.
+ * @bdev: The TTM device.
+ * @tt: The struct ttm_tt.
+ * @flags: Flags that govern the backup behaviour.
+ *
+ * Update the page accounting and call ttm_pool_shrink_tt to free
pages
+ * or back them up.
+ *
+ * Return: Number of pages freed or swapped out, or negative error
code on
+ * error.
+ */
+long ttm_tt_backup(struct ttm_device *bdev, struct ttm_tt *tt,
+		   const struct ttm_backup_flags flags)
+{
+	long ret;
+
+	if (WARN_ON(IS_ERR_OR_NULL(tt->backup)))
+		return 0;
+
+	ret = ttm_pool_backup_tt(&bdev->pool, tt, &flags);
+
+	if (ret > 0)
+		tt->page_flags &= ~TTM_TT_FLAG_PRIV_POPULATED;
+
+	return ret;
+}
+
  /**
   * ttm_tt_swapout - swap out tt object
   *
diff --git a/include/drm/ttm/ttm_pool.h
b/include/drm/ttm/ttm_pool.h
index 160d954a261e..3112a4be835c 100644
--- a/include/drm/ttm/ttm_pool.h
+++ b/include/drm/ttm/ttm_pool.h
@@ -33,6 +33,7 @@
  
  struct device;
  struct seq_file;
+struct ttm_backup_flags;
  struct ttm_operation_ctx;
  struct ttm_pool;
  struct ttm_tt;
@@ -89,6 +90,11 @@ void ttm_pool_fini(struct ttm_pool *pool);
  
  int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m);
  
+void ttm_pool_release_backed_up(struct ttm_tt *tt);
+
+long ttm_pool_backup_tt(struct ttm_pool *pool, struct ttm_tt *ttm,
+			const struct ttm_backup_flags *flags);
+
  int ttm_pool_mgr_init(unsigned long num_pages);
  void ttm_pool_mgr_fini(void);
  
diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h
index 991edafdb2dd..6ca2fc7b2a26 100644
--- a/include/drm/ttm/ttm_tt.h
+++ b/include/drm/ttm/ttm_tt.h
@@ -32,11 +32,13 @@
  #include <drm/ttm/ttm_caching.h>
  #include <drm/ttm/ttm_kmap_iter.h>
  
+struct ttm_backup;
  struct ttm_device;
  struct ttm_tt;
  struct ttm_resource;
  struct ttm_buffer_object;
  struct ttm_operation_ctx;
+struct ttm_pool_tt_restore;
  
  /**
   * struct ttm_tt - This is a structure holding the pages,
caching- and aperture
@@ -88,6 +90,9 @@ struct ttm_tt {
  	 * TTM_TT_FLAG_PRIV_POPULATED: TTM internal only. DO NOT
USE. This is
  	 * set by TTM after ttm_tt_populate() has successfully
returned, and is
  	 * then unset when TTM calls ttm_tt_unpopulate().
+	 *
+	 * TTM_TT_FLAG_PRIV_BACKED_UP: TTM internal only. This is
set if the
+	 * struct ttm_tt has been (possibly partially) backed up.
  	 */
  #define TTM_TT_FLAG_SWAPPED		BIT(0)
  #define TTM_TT_FLAG_ZERO_ALLOC		BIT(1)
@@ -96,6 +101,7 @@ struct ttm_tt {
  #define TTM_TT_FLAG_DECRYPTED		BIT(4)
  
  #define TTM_TT_FLAG_PRIV_POPULATED	BIT(5)
+#define TTM_TT_FLAG_PRIV_BACKED_UP	BIT(6)
  	uint32_t page_flags;
  	/** @num_pages: Number of pages in the page array. */
  	uint32_t num_pages;
@@ -105,11 +111,20 @@ struct ttm_tt {
  	dma_addr_t *dma_address;
  	/** @swap_storage: Pointer to shmem struct file for swap
storage. */
  	struct file *swap_storage;
+	/**
+	 * @backup: Pointer to backup struct for backed up tts.
+	 * Could be unified with @swap_storage. Meanwhile, the
driver's
+	 * ttm_tt_create() callback is responsible for assigning
+	 * this field.
+	 */
+	struct ttm_backup *backup;
  	/**
  	 * @caching: The current caching state of the pages, see
enum
  	 * ttm_caching.
  	 */
  	enum ttm_caching caching;
+	/** @restore: Partial restoration from backup state. TTM
private */
+	struct ttm_pool_tt_restore *restore;
  };
  
  /**
@@ -131,7 +146,7 @@ static inline bool ttm_tt_is_populated(struct
ttm_tt *tt)
  
  static inline bool ttm_tt_is_swapped(const struct ttm_tt *tt)
  {
-	return tt->page_flags & TTM_TT_FLAG_SWAPPED;
+	return tt->page_flags & (TTM_TT_FLAG_SWAPPED |
TTM_TT_FLAG_PRIV_BACKED_UP);
  }
  
  /**
@@ -235,6 +250,21 @@ void ttm_tt_mgr_init(unsigned long num_pages,
unsigned long num_dma32_pages);
  struct ttm_kmap_iter *ttm_kmap_iter_tt_init(struct
ttm_kmap_iter_tt *iter_tt,
  					    struct ttm_tt *tt);
  unsigned long ttm_tt_pages_limit(void);
+
+/**
+ * struct ttm_backup_flags - Flags to govern backup behaviour.
+ * @purge: Free pages without backing up. Bypass pools.
+ * @writeback: Attempt to copy contents directly to swap space,
even
+ * if that means blocking on writes to external memory.
+ */
+struct ttm_backup_flags {
+	u32 purge : 1;
+	u32 writeback : 1;
+};
+
+long ttm_tt_backup(struct ttm_device *bdev, struct ttm_tt *tt,
+		   const struct ttm_backup_flags flags);
+
  #if IS_ENABLED(CONFIG_AGP)
  #include <linux/agp_backend.h>
  

      

    


[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux