From: Kunwu Chan <kunwu.chan@gmail.com>
To: Baoquan He <hebaoquan@kylinos.cn>
Cc: Kunwu Chan <kunwu.chan@gmail.com>,
linux-mm@kvack.org, akpm@linux-foundation.org, chrisl@kernel.org,
kasong@tencent.com, nphamcs@gmail.com, baohua@kernel.org,
youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org,
shikemeng@huaweicloud.com, chengming.zhou@linux.dev,
baoquan.he@linux.dev, david@kernel.org,
linux-kernel@vger.kernel.org, Kunwu Chan <chentao@kylinos.cn>
Subject: Re: [PATCH v2 01/12] mm: xswap support for zswap
Date: Tue, 15 Sep 2026 18:03:06 +0800 [thread overview]
Message-ID: <20260915100308.1599989-1-kunwu.chan@gmail.com> (raw)
In-Reply-To: <20260913075014.1732524-2-hebaoquan@kylinos.cn>
On Sun, 13 Sep 2026 15:50:03 +0800 Baoquan He <hebaoquan@kylinos.cn> wrote:
> From: Chris Li <chrisl@kernel.org>
>
> Introduce extendable swap device support - xswap.
>
> An xswap device has no backing storage and no swap data section, so
> it wastes no disk space. Creation is via a sysfs interface added in a
> later patch.
>
> Zswap writeback is gated on whether a real (non-xswap) swap device is
> active. nr_real_swapfiles counts such devices and is maintained at
> swapon/swapoff only, so the gate reflects "a device exists to write
> back to" rather than "a device currently has free slots". This keeps
> writeback working even when the real swap device is full, and avoids
> a double decrement when a full device is swapped off.
>
> Co-developed-by: Baoquan He <hebaoquan@kylinos.cn>
> Signed-off-by: Baoquan He <hebaoquan@kylinos.cn>
> Signed-off-by: Chris Li <chrisl@kernel.org>
> ---
> include/linux/swap.h | 2 ++
> mm/page_io.c | 16 ++++++++++++++++
> mm/swap_state.c | 7 +++++++
> mm/swapfile.c | 38 +++++++++++++++++++++++++++++++++++---
> mm/zswap.c | 7 ++++++-
> 5 files changed, 66 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/swap.h b/include/linux/swap.h
> index 5658a1634b85..787fe463dcbb 100644
> --- a/include/linux/swap.h
> +++ b/include/linux/swap.h
> @@ -207,6 +207,7 @@ enum {
> SWP_STABLE_WRITES = (1 << 11), /* no overwrite PG_writeback pages */
> SWP_SYNCHRONOUS_IO = (1 << 12), /* synchronous IO is efficient */
> SWP_HIBERNATION = (1 << 13), /* pinned for hibernation */
> + SWP_XSWAP = (1 << 14), /* extendable swap device */
> /* add others here before... */
> };
>
> @@ -356,6 +357,7 @@ void free_folio_and_swap_cache(struct folio *folio);
> void free_pages_and_swap_cache(struct encoded_page **, int);
> /* linux/mm/swapfile.c */
> extern atomic_long_t nr_swap_pages;
> +extern atomic_t nr_real_swapfiles;
> extern long total_swap_pages;
> extern atomic_t nr_rotate_swap;
>
> diff --git a/mm/page_io.c b/mm/page_io.c
> index 88962571cb93..5483c943e3e3 100644
> --- a/mm/page_io.c
> +++ b/mm/page_io.c
> @@ -248,6 +248,17 @@ int swap_writeout(struct swap_io_ctx *ctx, struct folio *folio)
> }
> rcu_read_unlock();
>
> + /*
> + * ctx->sis is set by swap_add_folio() which is called from
> + * __swap_writepage() below. Since we must avoid the writepage
> + * path for xswap devices, use the swap_info from the folio's
> + * swap entry directly instead of going through ctx.
> + */
> + if (unlikely(__swap_entry_to_info(folio->swap)->flags & SWP_XSWAP)) {
> + folio_mark_dirty(folio);
> + return AOP_WRITEPAGE_ACTIVATE;
> + }
> +
> __swap_writepage(ctx, folio);
> return 0;
> out_unlock:
> @@ -480,6 +491,11 @@ void swap_read_folio(struct swap_io_ctx *ctx, struct folio *folio)
> if (zswap_load(folio) != -ENOENT)
> goto finish;
>
> + if (unlikely(sis->flags & SWP_XSWAP)) {
> + folio_unlock(folio);
> + goto finish;
> + }
> +
> /* We have to read from slower devices. Increase zswap protection. */
> zswap_folio_swapin(folio);
> swap_add_folio(ctx, folio, READ);
> diff --git a/mm/swap_state.c b/mm/swap_state.c
> index b76eb3d876fd..2eedb7a3d7bb 100644
> --- a/mm/swap_state.c
> +++ b/mm/swap_state.c
> @@ -830,6 +830,13 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask,
> struct blk_plug plug;
> swp_entry_t ra_entry;
>
> + /*
> + * The entry may have been freed by another task. Avoid swap_info_get()
> + * which will print error message if the race happens.
> + */
> + if (si->flags & SWP_XSWAP)
> + goto skip;
> +
> mask = swapin_nr_pages(offset) - 1;
> if (!mask)
> goto skip;
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index 53bf01d5f7f1..193b08a54908 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -66,6 +66,7 @@ static void move_cluster(struct swap_info_struct *si,
> static DEFINE_SPINLOCK(swap_lock);
> static unsigned int nr_swapfiles;
> atomic_long_t nr_swap_pages;
> +atomic_t nr_real_swapfiles;
> /*
> * Some modules use swappable objects and may try to swap them out under
> * memory pressure (via the shrinker). Before doing so, they may wish to
> @@ -1208,6 +1209,9 @@ static void del_from_avail_list(struct swap_info_struct *si, bool swapoff)
> */
> lockdep_assert_held(&si->lock);
> si->flags &= ~SWP_WRITEOK;
> + /* Count active devices, not merely those on the avail list. */
> + if (!(si->flags & SWP_XSWAP))
> + atomic_sub(1, &nr_real_swapfiles);
> atomic_long_or(SWAP_USAGE_OFFLIST_BIT, &si->inuse_pages);
> } else {
> /*
> @@ -1265,6 +1269,8 @@ static void add_to_avail_list(struct swap_info_struct *si, bool swapon)
> }
>
> plist_add(&si->avail_list, &swap_avail_head);
> + if (swapon && !(si->flags & SWP_XSWAP))
> + atomic_add(1, &nr_real_swapfiles);
>
> skip:
> spin_unlock(&swap_avail_lock);
> @@ -2959,6 +2965,19 @@ static int setup_swap_extents(struct swap_info_struct *sis,
> struct inode *inode = mapping->host;
> int ret;
>
> + if (sis->flags & SWP_XSWAP) {
> + *span = 0;
> + /*
> + * xswap devices have no backing block device and
> + * physical writeout is skipped in swap_writeout(),
> + * but sis->ops must still be set so that callers
> + * like shrink_folio_list() can safely dereference
> + * ops->flags.
> + */
> + sis->ops = &swap_bdev_ops;
> + return 0;
> + }
> +
> ret = sio_pool_init();
> if (ret)
> return ret;
> @@ -3167,7 +3186,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
>
> destroy_swap_extents(p, p->swap_file);
>
> - if (!(p->flags & SWP_SOLIDSTATE))
> + if (!(p->flags & SWP_XSWAP) &&
> + !(p->flags & SWP_SOLIDSTATE))
> atomic_dec(&nr_rotate_swap);
>
> mutex_lock(&swapon_mutex);
> @@ -3277,6 +3297,19 @@ static void swap_stop(struct seq_file *swap, void *v)
> mutex_unlock(&swapon_mutex);
> }
>
> +static const char *swap_type_str(struct swap_info_struct *si)
> +{
> + struct file *file = si->swap_file;
> +
> + if (si->flags & SWP_XSWAP)
> + return "xswap\t";
> +
> + if (S_ISBLK(file_inode(file)->i_mode))
> + return "partition";
> +
> + return "file\t";
> +}
> +
> static int swap_show(struct seq_file *swap, void *v)
> {
> struct swap_info_struct *si = v;
> @@ -3296,8 +3329,7 @@ static int swap_show(struct seq_file *swap, void *v)
> len = seq_file_path(swap, file, " \t\n\\");
> seq_printf(swap, "%*s%s\t%lu\t%s%lu\t%s%d\n",
> len < 40 ? 40 - len : 1, " ",
> - S_ISBLK(file_inode(file)->i_mode) ?
> - "partition" : "file\t",
> + swap_type_str(si),
> bytes, bytes < 10000000 ? "\t" : "",
> inuse, inuse < 10000000 ? "\t" : "",
> si->prio);
> diff --git a/mm/zswap.c b/mm/zswap.c
> index b9948d4657d2..064970a4393f 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -1000,6 +1000,11 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
> if (!si)
> return -ENOENT;
>
> + if (si->flags & SWP_XSWAP) {
> + put_swap_device(si);
> + return -EINVAL;
> + }
> +
> mpol = get_task_policy(current);
> folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol,
> NO_INTERLEAVE_INDEX);
> @@ -1545,7 +1550,7 @@ bool zswap_store(struct folio *folio)
> zswap_pool_put(pool);
> put_objcg:
> obj_cgroup_put(objcg);
> - if (!ret && zswap_pool_reached_full)
> + if (!ret && zswap_pool_reached_full && atomic_read(&nr_real_swapfiles))
> queue_work(shrink_wq, &zswap_shrink_work);
> check_old:
> /*
> --
> 2.54.0
>
>
The SWP_XSWAP flag and nr_real_swapfiles accounting look correct. The writeback guard
in page_io.c (both swap_writeout and swap_read_folio paths) properly short-circuits
xswap devices, and the zswap writeback gating on nr_real_swapfiles ensures writeback
only fires when a real swap device is active.
No issues found.
Reviewed-by: Kunwu Chan <chentao@kylinos.cn>
Thanks,
Kunwu
next prev parent reply other threads:[~2026-09-15 10:03 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-13 7:50 [PATCH v2 00/12] mm, swap: extendable swap devices (xswap) Baoquan He
2026-09-13 7:50 ` [PATCH v2 01/12] mm: xswap support for zswap Baoquan He
2026-09-15 10:03 ` Kunwu Chan [this message]
2026-09-16 2:52 ` Baoquan He
2026-09-16 3:04 ` KunWu Chan
2026-09-13 7:50 ` [PATCH v2 02/12] mm, swap: add CONFIG_XSWAP and xswap fields to swap_info_struct Baoquan He
2026-09-13 7:50 ` [PATCH v2 03/12] mm, swap: refactor free_swap_cluster_info to take swap_info_struct Baoquan He
2026-09-13 7:50 ` [PATCH v2 04/12] mm, swap: add xswap cluster grow via VM_SPARSE vmalloc Baoquan He
2026-09-13 7:50 ` [PATCH v2 05/12] mm, swap: add sysfs create interface for xswap Baoquan He
2026-09-13 7:50 ` [PATCH v2 06/12] mm, swap: add xswap grow trigger on cluster allocation Baoquan He
2026-09-13 7:50 ` [PATCH v2 07/12] mm, swap: add xswap_try_shrink and shrink trigger on cluster free Baoquan He
2026-09-13 7:50 ` [PATCH v2 08/12] mm, swap: free backing pages in xswap_unmap_clusters Baoquan He
2026-09-13 7:50 ` [PATCH v2 09/12] mm, swap: defer xswap shrink to workqueue to avoid lock recursion Baoquan He
2026-09-13 7:50 ` [PATCH v2 10/12] mm, swap: refactor swapoff + add xswap_destroy Baoquan He
2026-09-13 7:50 ` [PATCH v2 11/12] mm, swap: require zswap for xswap devices Baoquan He
2026-09-13 7:50 ` [PATCH v2 12/12] mm, swap: add sysfs per-device size limit for xswap Baoquan He
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=20260915100308.1599989-1-kunwu.chan@gmail.com \
--to=kunwu.chan@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=chengming.zhou@linux.dev \
--cc=chentao@kylinos.cn \
--cc=chrisl@kernel.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=hebaoquan@kylinos.cn \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®