summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/ttm/ttm_backup.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/ttm/ttm_backup.c')
-rw-r--r--drivers/gpu/drm/ttm/ttm_backup.c123
1 files changed, 82 insertions, 41 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 <drm/ttm/ttm_backup.h>
#include <linux/export.h>
-#include <linux/page-flags.h>
#include <linux/swap.h>
+#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;
+ int ret, i;
- to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
- if (IS_ERR(to_folio))
- return PTR_ERR(to_folio);
+ *nr_pages_backed = 0;
- 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);
+ for (i = 0; i < nr_pages; ) {
+ int to_nr, 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 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);
}
/**