[RFC PATCH 07/10] mm/page_io: forward ghost swap reads to physical device via Redirect

From: Baoquan He

Date: Tue Jul 07 2026 - 04:28:44 EST


When swap_read_folio() encounters a ghost swap entry (SWP_GHOST),
check if there is a Redirect mapping to a physical swap slot via
ghost_redirect_to_phys(). If a physical backing exists, forward
the I/O to that device:

1. Query the redirect_xa / swap table for the physical swp_entry_t.
2. Temporarily redirect folio->swap to the physical entry so the
existing swap_read_folio_bdev_{sync,async} / swap_read_folio_fs
path computes the correct sector from the physical offset.
3. Restore folio->swap to the original ghost entry after I/O.

This completes the backend-switching round trip:
- Swapout: PTE -> ghost entry -> zswap_store -> Pointer(zswap_entry)
- Writeback: Pointer -> decompress -> write to phys -> Redirect(phys)
- Swapin: PTE -> ghost entry -> Redirect -> read from phys device

Signed-off-by: Baoquan He <baoquan.he@xxxxxxxxx>
---
mm/page_io.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/mm/page_io.c b/mm/page_io.c
index 96e776f9a503..b87b46f05b2f 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -697,6 +697,39 @@ void swap_read_folio(struct folio *folio, struct swap_iocb **plug, void *zentry)
goto finish;

if (unlikely(sis->flags & SWP_GHOST)) {
+ swp_entry_t phys_entry;
+ struct swap_info_struct *phys_si;
+
+ /*
+ * Ghost swap entry: check if there's a Redirect to a
+ * physical swap slot. If so, read from the physical
+ * device instead.
+ */
+ phys_entry = ghost_redirect_to_phys(sis, swp_offset(folio->swap));
+ if (phys_entry.val) {
+ phys_si = __swap_entry_to_info(phys_entry);
+ if (phys_si && phys_si->bdev) {
+ swp_entry_t saved = folio->swap;
+
+ /*
+ * Temporarily point the folio to the
+ * physical entry so the bdev read path
+ * computes the correct sector.
+ */
+ folio->swap = phys_entry;
+ zswap_folio_swapin(folio);
+ if (data_race(phys_si->flags & SWP_FS_OPS))
+ swap_read_folio_fs(folio, plug);
+ else if (phys_si->flags & SWP_SYNCHRONOUS_IO)
+ swap_read_folio_bdev_sync(folio,
+ phys_si);
+ else
+ swap_read_folio_bdev_async(folio,
+ phys_si);
+ folio->swap = saved;
+ goto finish;
+ }
+ }
folio_unlock(folio);
goto finish;
}
--
2.54.0