From: Nhat Pham <nphamcs@gmail.com>
To: akpm@linux-foundation.org
Cc: chrisl@kernel.org, kasong@tencent.com, hannes@cmpxchg.org,
mhocko@kernel.org, roman.gushchin@linux.dev,
shakeel.butt@linux.dev, yosry@kernel.org, david@kernel.org,
muchun.song@linux.dev, shikemeng@huaweicloud.com,
baoquan.he@linux.dev, baohua@kernel.org, youngjun.park@lge.com,
chengming.zhou@linux.dev, ljs@kernel.org, liam@infradead.org,
vbabka@kernel.org, rppt@kernel.org, surenb@google.com,
qi.zheng@linux.dev, axelrasmussen@google.com, yuanchu@google.com,
weixugc@google.com, riel@surriel.com, gourry@gourry.net,
haowenchao22@gmail.com, corbet@lwn.net, hughd@google.com,
baolin.wang@linux.alibaba.com, tj@kernel.org, mkoutny@suse.com,
skhan@linuxfoundation.org, kunwu.chan@linux.dev,
kernel-team@meta.com, nphamcs@gmail.com, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
cgroups@vger.kernel.org
Subject: [PATCH v4 06/11] mm, swap: write back vswap zswap entries to physical swap
Date: Tue, 25 Aug 2026 08:32:32 -0700 [thread overview]
Message-ID: <20260825153238.2695446-7-nphamcs@gmail.com> (raw)
In-Reply-To: <20260825153238.2695446-1-nphamcs@gmail.com>
Add support for writing back zswap-backed vswap entries to physical
swap. The mechanism mirrors the existing zswap writeback path, except
the backing physical slot is allocated on demand at writeback time
rather than already being pinned by the PTE.
The zswap shrinker no longer skips vswap entries, unless we are out of
physical swap space.
Signed-off-by: Nhat Pham <nphamcs@gmail.com>
---
mm/zswap.c | 67 +++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 46 insertions(+), 21 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index f16c0b44b5d5..70ad8010f18a 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1007,12 +1007,13 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)
static int zswap_writeback_entry(struct zswap_entry *entry,
swp_entry_t swpentry)
{
- struct xarray *tree;
pgoff_t offset = swp_offset(swpentry);
struct folio *folio;
struct mempolicy *mpol;
struct swap_info_struct *si;
struct swap_io_ctx ctx = {};
+ swp_entry_t phys = {};
+ bool is_vswap;
int ret = 0;
/* try to allocate swap cache folio */
@@ -1020,12 +1021,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
if (!si)
return -EEXIST;
- /* Vswap entries have no physical backing to write to. */
- if (swap_is_vswap(si)) {
- put_swap_device(si);
- return -EINVAL;
- }
-
+ is_vswap = swap_is_vswap(si);
mpol = get_task_policy(current);
folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol,
NO_INTERLEAVE_INDEX);
@@ -1044,24 +1040,44 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
/*
* folio is locked, and the swapcache is now secured against
* concurrent swapping to and from the slot, and concurrent
- * swapoff so we can safely dereference the zswap tree here.
+ * swapoff so we can safely dereference the zswap tree (or vswap
+ * vtable) here.
* Verify that the swap entry hasn't been invalidated and recycled
* behind our backs, to avoid overwriting a new swap folio with
* old compressed data. Only when this is successful can the entry
* be dereferenced.
*/
- tree = swap_zswap_tree(swpentry);
- if (entry != xa_load(tree, offset)) {
+ if (entry != zswap_entry_load(swpentry)) {
ret = -ENOMEM;
goto out;
}
+ if (is_vswap) {
+ /*
+ * Allocate physical backing before decompress so a failure
+ * wastes no work.
+ */
+ phys = folio_realloc_swap(folio);
+ if (!phys.val) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
if (!zswap_decompress(entry, folio)) {
ret = -EIO;
+ /*
+ * The phys allocation above took the entry out of the vtable.
+ * Restore the zswap entry to the vtable, which also frees the
+ * allocated physical swap space.
+ */
+ if (is_vswap)
+ vswap_zswap_store(swpentry, entry);
goto out;
}
- xa_erase(tree, offset);
+ if (!is_vswap)
+ xa_erase(swap_zswap_tree(swpentry), offset);
count_vm_event(ZSWPWB);
if (entry->objcg)
@@ -1076,7 +1092,10 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
folio_set_reclaim(folio);
/* start writeback */
- __swap_writepage(&ctx, folio, folio->swap);
+ if (is_vswap)
+ __swap_writepage(&ctx, folio, phys);
+ else
+ __swap_writepage(&ctx, folio, folio->swap);
swap_write_submit(&ctx);
out:
@@ -1091,6 +1110,15 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
/*********************************
* shrinker functions
**********************************/
+/*
+ * vswap zswap entries get a physical slot allocated on demand at writeback
+ * time. Skip the shrinker when none is available.
+ */
+static bool zswap_writeback_possible(void)
+{
+ return !vswap_is_enabled() || get_nr_swap_pages() > 0;
+}
+
/*
* The dynamic shrinker is modulated by the following factors:
*
@@ -1228,7 +1256,7 @@ static unsigned long zswap_shrinker_count(struct shrinker *shrinker,
if (!zswap_shrinker_enabled || !mem_cgroup_zswap_writeback_enabled(memcg))
return 0;
- if (vswap_is_enabled())
+ if (!zswap_writeback_possible())
return 0;
/*
@@ -1314,7 +1342,8 @@ static struct shrinker *zswap_alloc_shrinker(void)
* were scanned but none could be written back, or -ENOENT if @memcg has
* writeback disabled, is a zombie cgroup, or has empty zswap LRUs.
*
- * Also returns -ENOENT when vswap is enabled.
+ * Also returns -ENOENT when vswap is enabled and there is no physical
+ * swap to write back to.
*/
static int shrink_memcg(struct mem_cgroup *memcg)
{
@@ -1323,7 +1352,7 @@ static int shrink_memcg(struct mem_cgroup *memcg)
if (!mem_cgroup_zswap_writeback_enabled(memcg))
return -ENOENT;
- if (vswap_is_enabled())
+ if (!zswap_writeback_possible())
return -ENOENT;
/*
@@ -1354,11 +1383,7 @@ static void shrink_worker(struct work_struct *w)
int ret, failures = 0, attempts = 0;
unsigned long thr;
- /*
- * When vswap is enabled, zswap entries are almost all vswap backed,
- * with no slot to write back to.
- */
- if (vswap_is_enabled())
+ if (!zswap_writeback_possible())
return;
/* Reclaim down to the accept threshold */
@@ -1439,7 +1464,7 @@ static void shrink_worker(struct work_struct *w)
break;
resched:
cond_resched();
- } while (zswap_total_pages() > thr);
+ } while (zswap_total_pages() > thr && zswap_writeback_possible());
}
/*********************************
--
2.53.0-Meta
next prev parent reply other threads:[~2026-08-25 15:32 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 15:32 [PATCH v4 00/11] Virtual Swap Space (Swap Table Edition) Nhat Pham
2026-08-25 15:32 ` [PATCH v4 01/11] mm, swap: add virtual swap device infrastructure Nhat Pham
2026-08-25 15:32 ` [PATCH v4 02/11] mm, swap: support zswap and zero-filled swap pages as vswap backends Nhat Pham
2026-08-25 15:32 ` [PATCH v4 03/11] mm, swap: prepare the swap IO path for vswap Nhat Pham
2026-08-25 15:32 ` [PATCH v4 04/11] mm, swap: support physical swap as a vswap backend Nhat Pham
2026-08-25 15:32 ` [PATCH v4 05/11] mm, swap: enable THP swapin for vswap entries Nhat Pham
2026-08-25 15:32 ` Nhat Pham [this message]
2026-08-25 15:32 ` [PATCH v4 07/11] mm, swap: reclaim physical slots backing cache-only " Nhat Pham
2026-08-25 15:32 ` [PATCH v4 08/11] mm, swap: only charge physical swap entries Nhat Pham
2026-08-25 15:32 ` [PATCH v4 09/11] mm, swap: add debugfs counters for vswap Nhat Pham
2026-08-25 15:32 ` [PATCH v4 10/11] mm, swap: defer memcg_table allocation for physical swap clusters Nhat Pham
2026-08-25 15:32 ` [PATCH v4 11/11] mm, swap: widen swap_info_struct max/pages to unsigned long Nhat Pham
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=20260825153238.2695446-7-nphamcs@gmail.com \
--to=nphamcs@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=axelrasmussen@google.com \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=baoquan.he@linux.dev \
--cc=cgroups@vger.kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=chrisl@kernel.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=gourry@gourry.net \
--cc=hannes@cmpxchg.org \
--cc=haowenchao22@gmail.com \
--cc=hughd@google.com \
--cc=kasong@tencent.com \
--cc=kernel-team@meta.com \
--cc=kunwu.chan@linux.dev \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=mkoutny@suse.com \
--cc=muchun.song@linux.dev \
--cc=qi.zheng@linux.dev \
--cc=riel@surriel.com \
--cc=roman.gushchin@linux.dev \
--cc=rppt@kernel.org \
--cc=shakeel.butt@linux.dev \
--cc=shikemeng@huaweicloud.com \
--cc=skhan@linuxfoundation.org \
--cc=surenb@google.com \
--cc=tj@kernel.org \
--cc=vbabka@kernel.org \
--cc=weixugc@google.com \
--cc=yosry@kernel.org \
--cc=youngjun.park@lge.com \
--cc=yuanchu@google.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox