mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®