[PATCH v6 05/12] mm, swap: enable THP swapin for vswap entries
From: Nhat Pham
Date: Wed Oct 21 2026 - 02:09:58 EST
Swap a large anon folio back in as a unit when its vswap entries share a
contiguous run of physical swap slots on a synchronous IO device,
instead of always falling back to order-0 faults.
A zswap-backed or mixed-backing batch is still refused, and the fault
retries at a smaller order.
Signed-off-by: Nhat Pham <nphamcs@xxxxxxxxx>
---
mm/memory.c | 5 +++--
mm/swap_state.c | 17 +++++++++++++----
mm/zswap.c | 6 +++++-
3 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/mm/memory.c b/mm/memory.c
index 9f651d247eef..430b83900ba8 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4885,9 +4885,10 @@ static unsigned long thp_swapin_suitable_orders(struct vm_fault *vmf)
* lack handling for such cases, so fallback to swapping in order-0
* folio.
*
- * THP swapin for vswap is not supported yet either.
+ * Vswap entries are checked later, under the cluster lock in
+ * __swap_cache_add_check().
*/
- if (is_vswap_entry(entry) || !zswap_never_enabled())
+ if (!is_vswap_entry(entry) && !zswap_never_enabled())
return 0;
/*
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 3900975c675f..6c8e8f49e349 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -174,6 +174,9 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci,
unsigned int ci_off, ci_end;
unsigned long old_tb;
bool is_zero;
+ struct swap_cluster_info_dynamic *ci_dyn;
+ enum vswap_backing_type type;
+ int ret;
lockdep_assert_held(&ci->lock);
@@ -202,11 +205,17 @@ static int __swap_cache_add_check(struct swap_cluster_info *ci,
return 0;
/*
- * Reject a vswap batch so __swap_cache_alloc_folio falls back to
- * order 0.
+ * For a vswap entry batch, reject if the backing is not THP-amenable
+ * (e.g. uniformly ZSWAP, or mixed). The order-fallback loop in
+ * __swap_cache_alloc_folio will retry with a smaller order on -EBUSY.
*/
- if (is_vswap_entry(targ_entry))
- return -EBUSY;
+ if (is_vswap_entry(targ_entry)) {
+ ci_dyn = container_of(ci, struct swap_cluster_info_dynamic, ci);
+ ret = __vswap_check_backing(ci_dyn, round_down(ci_off, nr),
+ nr, &type);
+ if (ret != nr || type == VSWAP_ZSWAP)
+ return -EBUSY;
+ }
is_zero = __swap_table_test_zero(ci, ci_off);
ci_off = round_down(ci_off, nr);
diff --git a/mm/zswap.c b/mm/zswap.c
index 24c4e7a73d56..1b91dba992ab 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1753,9 +1753,13 @@ int zswap_load(struct folio *folio)
* range on the backing device, so scan the range rather than rejecting
* it outright. The caller has pinned every slot, so zswap cannot start
* a store or a writeback into the range while we look.
+ *
+ * A vswap batch is checked when the folio enters the swap cache, and
+ * its backing cannot change after that.
*/
if (folio_test_large(folio)) {
- if (WARN_ON_ONCE(zswap_is_present(swp,
+ if (WARN_ON_ONCE(!swap_is_vswap(si) &&
+ zswap_is_present(swp,
folio_nr_pages(folio)))) {
folio_unlock(folio);
return -EIO;
--
2.53.0-Meta