From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, chrisl@kernel.org, kasong@tencent.com,
shikemeng@huaweicloud.com, nphamcs@gmail.com, baohua@kernel.org,
youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org,
chengming.zhou@linux.dev, linux-kernel@vger.kernel.org,
Baoquan He <baoquan.he@linux.dev>
Subject: [RFC PATCH 06/10] mm/zswap: implement ghost-to-physical writeback for backend switching
Date: Tue, 7 Jul 2026 16:26:08 +0800 [thread overview]
Message-ID: <20260707082614.95030-7-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260707082614.95030-1-baoquan.he@linux.dev>
Add zswap_writeback_ghost() to handle zswap writeback when the
backing swap device is a ghost swapfile (SWP_GHOST). Previously,
writeback for ghost entries was rejected with -EINVAL.
The new writeback path:
1. Allocates a physical swap slot from any real swap device via
alloc_phys_swap_slot().
2. Creates a swap-cache folio attached to the ghost swap entry.
3. Decompresses the zswap entry into the folio.
4. Stores the ghost->physical mapping in the ghost's redirect_xa.
5. Temporarily redirects folio->swap to the physical entry and
calls __swap_writepage() to write to the physical device.
6. On success, the xarray mapping persists. When the swap-cache
folio is later removed, __swap_cache_do_del_folio() restores
the Redirect entry from the xarray.
This enables the backend switch from zswap (compressed in memory)
to physical swap (on disk) without changing the PTE — the PTE
continues to point to the ghost swap entry, and the swapin path
forwards the I/O via the Redirect entry.
Modify zswap_writeback_entry() to call zswap_writeback_ghost()
for ghost-backed entries instead of returning -EINVAL.
Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
mm/zswap.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index bdf8283fd880..2137e8f14413 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -978,6 +978,90 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)
* the swap cache, the compressed version stored by zswap can be
* freed.
*/
+/*
+ * Ghost swapfile writeback: allocate a physical swap slot, decompress
+ * the zswap entry into a swap-cache folio, write it to the physical
+ * device via __swap_writepage, and set up a Redirect entry on the
+ * ghost swap table so future swapins are forwarded to the physical
+ * device.
+ */
+static int zswap_writeback_ghost(struct zswap_entry *entry,
+ swp_entry_t swpentry,
+ struct swap_info_struct *ghost_si,
+ pgoff_t offset)
+{
+ swp_entry_t phys_entry;
+ struct folio *folio;
+ struct mempolicy *mpol;
+ swp_entry_t saved_entry;
+ int ret = 0;
+
+ /* Allocate a physical swap slot from a real device */
+ phys_entry = alloc_phys_swap_slot();
+ if (!phys_entry.val)
+ return -ENOMEM;
+
+ /* Create a swap-cache folio attached to the ghost swap entry */
+ mpol = get_task_policy(current);
+ folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol,
+ NO_INTERLEAVE_INDEX, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ goto free_phys;
+ }
+
+ /* Decompress zswap data into the folio */
+ if (!zswap_decompress(entry, folio)) {
+ ret = -EIO;
+ goto del_folio;
+ }
+
+ count_vm_event(ZSWPWB);
+ if (entry->objcg)
+ count_objcg_events(entry->objcg, ZSWPWB, 1);
+
+ zswap_entry_free(entry);
+
+ folio_mark_uptodate(folio);
+ folio_set_reclaim(folio);
+
+ /*
+ * Store the redirect mapping in the xarray so it persists
+ * across swap-cache PFN cycles. The swap table currently
+ * holds a PFN (the swap cache folio); when the folio is
+ * eventually removed from swap cache, the Redirect entry
+ * will be restored from the xarray.
+ */
+ xa_store(&ghost_si->redirect_xa, offset,
+ xa_mk_value(phys_entry.val), GFP_ATOMIC);
+
+ /*
+ * Write the folio to the physical swap device. Temporarily
+ * redirect folio->swap to the physical entry so
+ * __swap_writepage uses the correct bdev and sector.
+ */
+ saved_entry = folio->swap;
+ folio->swap = phys_entry;
+ ret = __swap_writepage(folio, NULL);
+ folio->swap = saved_entry;
+
+ if (ret) {
+ xa_erase(&ghost_si->redirect_xa, offset);
+ goto del_folio;
+ }
+
+ folio_put(folio);
+ return 0;
+
+del_folio:
+ swap_cache_del_folio(folio);
+ folio_unlock(folio);
+ folio_put(folio);
+free_phys:
+ free_phys_swap_slot(phys_entry);
+ return ret;
+}
+
static int zswap_writeback_entry(struct zswap_entry *entry,
swp_entry_t swpentry)
{
@@ -994,11 +1078,6 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
if (!si)
return -ENOENT;
- if (si->flags & SWP_GHOST) {
- put_swap_device(si);
- return -EINVAL;
- }
-
/*
* Verify the swap table Pointer entry still references this
* zswap_entry. If the entry was invalidated or replaced by a
@@ -1015,6 +1094,13 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
}
spin_unlock(&ci->lock);
+ /* Ghost swapfile: write back to a physical swap device */
+ if (si->flags & SWP_GHOST) {
+ ret = zswap_writeback_ghost(entry, swpentry, si, offset);
+ put_swap_device(si);
+ return ret;
+ }
+
mpol = get_task_policy(current);
folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol,
NO_INTERLEAVE_INDEX, NULL);
--
2.54.0
next prev parent reply other threads:[~2026-07-07 8:27 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 8:26 [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07 8:26 ` [RFC PATCH 01/10] mm: ghost swapfile support for zswap Baoquan He
2026-07-07 8:26 ` [RFC PATCH 02/10] mm/swap_table: add Redirect entry encoding for ghost swap backend switching Baoquan He
2026-07-07 8:26 ` [RFC PATCH 03/10] mm/swap: add redirect_xa field and ghost redirect helper declarations Baoquan He
2026-07-07 8:26 ` [RFC PATCH 04/10] mm/swapfile: implement ghost redirect helpers and free-path cascade Baoquan He
2026-07-07 8:26 ` [RFC PATCH 05/10] mm/swap_state: restore Redirect entry when swap cache folio is removed Baoquan He
2026-07-07 8:26 ` Baoquan He [this message]
2026-07-07 8:26 ` [RFC PATCH 07/10] mm/page_io: forward ghost swap reads to physical device via Redirect Baoquan He
2026-07-07 8:26 ` [RFC PATCH 08/10] mm/swapfile: manage ghost cluster_info via lazy vmalloc Baoquan He
2026-07-07 8:26 ` [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth Baoquan He
2026-07-07 22:36 ` Nhat Pham
2026-07-09 14:52 ` Baoquan He
2026-07-10 1:11 ` Nhat Pham
2026-07-11 9:02 ` Chris Li
2026-07-07 8:26 ` [RFC PATCH 10/10] mm/swapfile: add sysfs interface for ghost swap extension Baoquan He
2026-07-07 8:56 ` [RFC PATCH 00/10] mm/swap: ghost swapfile with backend switching via Redirect entries Baoquan He
2026-07-07 21:23 ` Nhat Pham
2026-07-07 21:25 ` Nhat Pham
2026-07-09 11:38 ` Baoquan He
2026-07-13 0:02 ` Nhat Pham
2026-07-13 17:47 ` Chris Li
2026-07-15 11:21 ` Baoquan He
2026-07-15 14:19 ` Baoquan He
2026-07-11 8:49 ` Chris Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260707082614.95030-7-baoquan.he@linux.dev \
--to=baoquan.he@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=chrisl@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=kasong@tencent.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nphamcs@gmail.com \
--cc=shikemeng@huaweicloud.com \
--cc=yosry@kernel.org \
--cc=youngjun.park@lge.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.