All of lore.kernel.org
 help / color / mirror / Atom feed
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 04/10] mm/swapfile: implement ghost redirect helpers and free-path cascade
Date: Tue,  7 Jul 2026 16:26:06 +0800	[thread overview]
Message-ID: <20260707082614.95030-5-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260707082614.95030-1-baoquan.he@linux.dev>

Add the core infrastructure for ghost swapfile backend switching:
- alloc_phys_swap_slot()
- free_phys_swap_slot()
- ghost_redirect_to_phys()

Initialize redirect_xa in read_swap_header() when a ghost swapfile
is detected (file size == PAGE_SIZE).

Handle Redirect entries in __swap_cluster_free_entries(): when a
ghost entry with a Redirect is freed (swap count reaches zero),
erase the xarray mapping and cascade the free to the physical
swap slot.  Also skip zero-flag clearing for Redirect entries
(they carry no zero flag).

Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
 mm/swapfile.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 105 insertions(+), 2 deletions(-)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index 148ceb2d812b..c1686c2b2521 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/kernel_stat.h>
 #include <linux/swap.h>
+#include <linux/xarray.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/namei.h>
@@ -1975,7 +1976,14 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,
 		 * means zswap has a compressed copy; the xarray still
 		 * holds the entry and zswap_invalidate will free it.
 		 */
-		if (swp_tb_is_pointer(old_tb)) {
+		if (swp_tb_is_redirect(old_tb)) {
+			swp_entry_t phys_entry;
+
+			phys_entry = redirect_to_swp_entry(old_tb);
+			xa_erase(&si->redirect_xa,
+				 ci_off + cluster_offset(si, ci));
+			free_phys_swap_slot(phys_entry);
+		} else if (swp_tb_is_pointer(old_tb)) {
 			VM_WARN_ON(zswap_swp_tb_get_count(old_tb) > 1);
 			/*
 			 * Free the zswap entry now under ci->lock while
@@ -1990,7 +1998,8 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,
 
 		/* Resetting the slot to NULL also clears the inline flags. */
 		__swap_table_set(ci, ci_off, null_to_swp_tb());
-		if (!SWAP_TABLE_HAS_ZEROFLAG && !swp_tb_is_pointer(old_tb))
+		if (!SWAP_TABLE_HAS_ZEROFLAG && !swp_tb_is_pointer(old_tb) &&
+		    !swp_tb_is_redirect(old_tb))
 			__swap_table_clear_zero(ci, ci_off);
 
 		/*
@@ -3593,6 +3602,7 @@ static unsigned long read_swap_header(struct swap_info_struct *si,
 		/* Ghost swapfile */
 		si->bdev = NULL;
 		si->flags |= SWP_GHOST | SWP_SOLIDSTATE;
+		xa_init(&si->redirect_xa);
 		return maxpages;
 	}
 
@@ -4006,6 +4016,99 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp)
 }
 #endif
 
+#ifdef CONFIG_ZSWAP
+/*
+ * Ghost swapfile redirect: physical swap slot allocation for writeback.
+ *
+ * Allocates a single order-0 swap slot from any real (non-ghost, writable)
+ * swap device.  Used by zswap writeback to obtain a physical destination
+ * for ghost-backed entries.
+ */
+swp_entry_t alloc_phys_swap_slot(void)
+{
+	struct swap_info_struct *si;
+	unsigned long offset;
+	swp_entry_t entry = {};
+
+	spin_lock(&swap_avail_lock);
+	plist_for_each_entry(si, &swap_avail_head, avail_list) {
+		/* Skip ghost or non-writable devices */
+		if (si->flags & SWP_GHOST)
+			continue;
+		if (!(si->flags & SWP_WRITEOK))
+			continue;
+		if (!get_swap_device_info(si))
+			continue;
+
+		/* Try to allocate one slot */
+		offset = cluster_alloc_swap_entry(si, NULL);
+		put_swap_device(si);
+		if (offset && offset != SWAP_ENTRY_INVALID) {
+			entry = swp_entry(si->type, offset);
+			break;
+		}
+	}
+	spin_unlock(&swap_avail_lock);
+
+	return entry;
+}
+
+void free_phys_swap_slot(swp_entry_t phys_entry)
+{
+	struct swap_info_struct *si;
+	unsigned long offset;
+	struct swap_cluster_info *ci;
+
+	si = get_swap_device(phys_entry);
+	if (!si)
+		return;
+	offset = swp_offset(phys_entry);
+	ci = swap_cluster_lock(si, offset);
+	if (ci) {
+		/*
+		 * Clear the physical slot.  The swap count should be 0 at
+		 * this point (the ghost entry's count has been transferred
+		 * or freed).
+		 */
+		__swap_table_set(ci, offset % SWAPFILE_CLUSTER,
+				null_to_swp_tb());
+		swap_cluster_unlock(ci);
+		swap_range_free(si, offset, 1);
+	}
+	put_swap_device(si);
+}
+
+/*
+ * Resolve a ghost swap offset to its physical backing entry.
+ * Checks the swap table first (for Redirect entries), then falls
+ * back to the xarray (for entries that have a swap-cache folio).
+ */
+swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset)
+{
+	struct swap_cluster_info *ci;
+	unsigned long swp_tb;
+	void *val;
+
+	/* Fast path: check the swap table for a Redirect entry */
+	ci = __swap_offset_to_cluster(si, offset);
+	spin_lock(&ci->lock);
+	swp_tb = __swap_table_get(ci, offset % SWAPFILE_CLUSTER);
+	if (swp_tb_is_redirect(swp_tb)) {
+		swp_entry_t phys = redirect_to_swp_entry(swp_tb);
+		spin_unlock(&ci->lock);
+		return phys;
+	}
+	spin_unlock(&ci->lock);
+
+	/* Slow path: look up the persistent xarray mapping */
+	val = xa_load(&si->redirect_xa, offset);
+	if (val && !xa_is_err(val))
+		return (swp_entry_t){ .val = xa_to_value(val) };
+
+	return (swp_entry_t){};
+}
+#endif /* CONFIG_ZSWAP */
+
 static int __init swapfile_init(void)
 {
 	swapfile_maximum_size = arch_max_swapfile_size();
-- 
2.54.0


  parent reply	other threads:[~2026-07-07  8:27 UTC|newest]

Thread overview: 22+ 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 ` Baoquan He [this message]
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 ` [RFC PATCH 06/10] mm/zswap: implement ghost-to-physical writeback for backend switching Baoquan He
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-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-5-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.