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 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.