[PATCH v2 1/8] mm/swapfile: add batched version of folio_dup_swap

From: Dev Jain

Date: Tue Sep 01 2026 - 01:44:48 EST


Add folio_dup_swap_pages to handle a batch of consecutive pages. Note
that folio_dup_swap already can handle a subset of this: nr_pages == 1 and
nr_pages == folio_nr_pages(folio). Generalize this to any nr_pages.

Currently we have a not-so-nice logic of passing in subpage == NULL if
we mean to exercise the logic on the entire folio, and subpage != NULL if
we want to exercise the logic on only that subpage. Remove this
indirection: the caller invokes folio_dup_swap_pages() if it wants to
operate on a range of pages in the folio (i.e nr_pages may be anything
between 1 and folio_nr_pages()), and invokes folio_dup_swap() if it
wants to operate on the entire folio.

Acked-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
Signed-off-by: Dev Jain <dev.jain@xxxxxxx>
Reviewed-by: Barry Song <baohua@xxxxxxxxxx>
---
mm/rmap.c | 2 +-
mm/shmem.c | 2 +-
mm/swap.h | 18 ++++++++++++++++--
mm/swapfile.c | 16 ++++++++--------
4 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/mm/rmap.c b/mm/rmap.c
index 606c8ecf31302..0a4aa06b66de6 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -2148,7 +2148,7 @@ static bool ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
swp_entry_t entry = page_swap_entry(page);
struct mm_struct *mm = vma->vm_mm;

- if (folio_dup_swap(folio, page) < 0)
+ if (folio_dup_swap_pages(folio, page, 1) < 0)
return false;

/*
diff --git a/mm/shmem.c b/mm/shmem.c
index 897fa2b61346f..6cea018d69f04 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1712,7 +1712,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
spin_unlock(&shmem_swaplist_lock);
}

- folio_dup_swap(folio, NULL);
+ folio_dup_swap(folio);
shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));

BUG_ON(folio_mapped(folio));
diff --git a/mm/swap.h b/mm/swap.h
index 90a551a88df63..f52ecbedae8b8 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -244,7 +244,8 @@ extern int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp);
* folio_put_swap(): does the opposite thing of folio_dup_swap().
*/
int folio_alloc_swap(struct folio *folio);
-int folio_dup_swap(struct folio *folio, struct page *page);
+int folio_dup_swap_pages(struct folio *folio, struct page *page,
+ unsigned long nr_pages);
void folio_put_swap(struct folio *folio, struct page *page);

/* For internal use */
@@ -368,7 +369,8 @@ static inline int folio_alloc_swap(struct folio *folio)
return -EINVAL;
}

-static inline int folio_dup_swap(struct folio *folio, struct page *page)
+static inline int folio_dup_swap_pages(struct folio *folio, struct page *page,
+ unsigned long nr_pages)
{
return -EINVAL;
}
@@ -463,6 +465,18 @@ static inline void __swap_cache_replace_folio(struct swap_cluster_info *ci,
}
#endif /* CONFIG_SWAP */

+/**
+ * folio_dup_swap() - Increase swap count of all swap entries of a folio.
+ * @folio: folio with swap entries bound.
+ *
+ * See folio_dup_swap_pages() for more information.
+ */
+static inline int folio_dup_swap(struct folio *folio)
+{
+ return folio_dup_swap_pages(folio, folio_page(folio, 0),
+ folio_nr_pages(folio));
+}
+
extern const struct swap_ops swap_bdev_ops;

int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 53bf01d5f7f11..25bb6fc562c57 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1785,9 +1785,10 @@ int folio_alloc_swap(struct folio *folio)
}

/**
- * folio_dup_swap() - Increase swap count of swap entries of a folio.
+ * folio_dup_swap_pages() - Increase swap count of swap entries of a folio.
* @folio: folio with swap entries bounded.
- * @page: if not NULL, only increase the swap count of this page.
+ * @page: the first page in the folio to increase the swap count for.
+ * @nr_pages: the number of pages in the folio to increase the swap count for.
*
* Typically called when the folio is unmapped and have its swap entry to
* take its place: Swap entries allocated to a folio has count == 0 and pinned
@@ -1801,18 +1802,17 @@ int folio_alloc_swap(struct folio *folio)
* swap_put_entries_direct on its swap entry before this helper returns, or
* the swap count may underflow.
*/
-int folio_dup_swap(struct folio *folio, struct page *page)
+int folio_dup_swap_pages(struct folio *folio, struct page *page,
+ unsigned long nr_pages)
{
swp_entry_t entry = folio->swap;
- unsigned long nr_pages = folio_nr_pages(folio);
+ unsigned long idx = folio_page_idx(folio, page);

VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio);
VM_WARN_ON_FOLIO(!folio_test_swapcache(folio), folio);
+ VM_WARN_ON_FOLIO(idx + nr_pages > folio_nr_pages(folio), folio);

- if (page) {
- entry.val += folio_page_idx(folio, page);
- nr_pages = 1;
- }
+ entry.val += idx;

return swap_dup_entries_cluster(swap_entry_to_info(entry),
swp_offset(entry), nr_pages);
--
2.43.0