From: Baoquan He <baoquan.he@linux.dev>
To: linux-mm@kvack.org
Cc: akpm@linux-foundation.org, chrisl@kernel.org,
usama.arif@linux.dev, baohua@kernel.org, kasong@tencent.com,
nphamcs@gmail.com, shikemeng@huaweicloud.com,
youngjun.park@lge.com, linux-kernel@vger.kernel.org,
Baoquan He <baoquan.he@linux.dev>
Subject: [PATCH v6 2/3] mm/swap: use swap_ops to register swap device's methods
Date: Tue, 12 May 2026 18:42:00 +0800 [thread overview]
Message-ID: <20260512104201.716213-3-baoquan.he@linux.dev> (raw)
In-Reply-To: <20260512104201.716213-1-baoquan.he@linux.dev>
This simplifies codes and makes logic clearer. And also makes later any
new swap device type being added easier to handle.
Currently there are three types of swap devices: bdev_fs, bdev_sync
and bdev_async, and only operations read_folio and write_folio are
included. In the future, there could be more swap device types added
and more appropriate opeations adapted into swap_ops.
Suggested-by: Chris Li <chrisl@kernel.org>
Acked-by: Chris Li <chrisl@kernel.org>
Co-developed-by: Barry Song <baohua@kernel.org>
Signed-off-by: Barry Song <baohua@kernel.org>
Signed-off-by: Baoquan He <baoquan.he@linux.dev>
---
include/linux/swap.h | 2 +
mm/swap.h | 10 ++++-
mm/swap_io.c | 102 +++++++++++++++++++++++++------------------
mm/swapfile.c | 9 ++++
mm/zswap.c | 2 +-
5 files changed, 80 insertions(+), 45 deletions(-)
diff --git a/include/linux/swap.h b/include/linux/swap.h
index aa89e1d30a77..85f15ec326c1 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -243,6 +243,7 @@ struct swap_sequential_cluster {
unsigned int next[SWAP_NR_ORDERS]; /* Likely next allocation offset */
};
+struct swap_ops;
/*
* The in-memory structure used to track swap areas.
*/
@@ -283,6 +284,7 @@ struct swap_info_struct {
struct work_struct reclaim_work; /* reclaim worker */
struct list_head discard_clusters; /* discard clusters list */
struct plist_node avail_list; /* entry in swap_avail_head */
+ const struct swap_ops *ops;
};
static inline swp_entry_t page_swap_entry(struct page *page)
diff --git a/mm/swap.h b/mm/swap.h
index 161185057993..29bdc679fa98 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -217,6 +217,15 @@ extern void __swap_cluster_free_entries(struct swap_info_struct *si,
/* linux/mm/swap_io.c */
int sio_pool_init(void);
struct swap_iocb;
+struct swap_ops {
+ void (*read_folio)(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug);
+ void (*write_folio)(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug);
+};
+int init_swap_ops(struct swap_info_struct *sis);
void swap_read_folio(struct folio *folio, struct swap_iocb **plug);
void __swap_read_unplug(struct swap_iocb *plug);
static inline void swap_read_unplug(struct swap_iocb *plug)
@@ -226,7 +235,6 @@ static inline void swap_read_unplug(struct swap_iocb *plug)
}
void swap_write_unplug(struct swap_iocb *sio);
int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug);
-void __swap_writepage(struct folio *folio, struct swap_iocb **swap_plug);
/* linux/mm/swap_state.c */
extern struct address_space swap_space __read_mostly;
diff --git a/mm/swap_io.c b/mm/swap_io.c
index 7c1a3a3a53f8..3f9aee5b6a4d 100644
--- a/mm/swap_io.c
+++ b/mm/swap_io.c
@@ -238,6 +238,7 @@ static void swap_zeromap_folio_clear(struct folio *folio)
int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug)
{
int ret = 0;
+ struct swap_info_struct *sis = __swap_entry_to_info(folio->swap);
if (folio_free_swap(folio))
goto out_unlock;
@@ -283,7 +284,7 @@ int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug)
}
rcu_read_unlock();
- __swap_writepage(folio, swap_plug);
+ sis->ops->write_folio(sis, folio, swap_plug);
return 0;
out_unlock:
folio_unlock(folio);
@@ -373,10 +374,11 @@ static void sio_write_complete(struct kiocb *iocb, long ret)
mempool_free(sio, sio_pool);
}
-static void swap_writepage_fs(struct folio *folio, struct swap_iocb **swap_plug)
+static void swap_writepage_fs(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **swap_plug)
{
struct swap_iocb *sio = swap_plug ? *swap_plug : NULL;
- struct swap_info_struct *sis = __swap_entry_to_info(folio->swap);
struct file *swap_file = sis->swap_file;
loff_t pos = swap_dev_pos(folio->swap);
@@ -409,8 +411,9 @@ static void swap_writepage_fs(struct folio *folio, struct swap_iocb **swap_plug)
*swap_plug = sio;
}
-static void swap_writepage_bdev_sync(struct folio *folio,
- struct swap_info_struct *sis)
+static void swap_writepage_bdev_sync(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug)
{
struct bio_vec bv;
struct bio bio;
@@ -429,8 +432,9 @@ static void swap_writepage_bdev_sync(struct folio *folio,
__end_swap_bio_write(&bio);
}
-static void swap_writepage_bdev_async(struct folio *folio,
- struct swap_info_struct *sis)
+static void swap_writepage_bdev_async(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug)
{
struct bio *bio;
@@ -446,29 +450,6 @@ static void swap_writepage_bdev_async(struct folio *folio,
submit_bio(bio);
}
-void __swap_writepage(struct folio *folio, struct swap_iocb **swap_plug)
-{
- struct swap_info_struct *sis = __swap_entry_to_info(folio->swap);
-
- VM_BUG_ON_FOLIO(!folio_test_swapcache(folio), folio);
- /*
- * ->flags can be updated non-atomically,
- * but that will never affect SWP_FS_OPS, so the data_race
- * is safe.
- */
- if (data_race(sis->flags & SWP_FS_OPS))
- swap_writepage_fs(folio, swap_plug);
- /*
- * ->flags can be updated non-atomically,
- * but that will never affect SWP_SYNCHRONOUS_IO, so the data_race
- * is safe.
- */
- else if (data_race(sis->flags & SWP_SYNCHRONOUS_IO))
- swap_writepage_bdev_sync(folio, sis);
- else
- swap_writepage_bdev_async(folio, sis);
-}
-
void swap_write_unplug(struct swap_iocb *sio)
{
struct iov_iter from;
@@ -537,9 +518,10 @@ static bool swap_read_folio_zeromap(struct folio *folio)
return true;
}
-static void swap_read_folio_fs(struct folio *folio, struct swap_iocb **plug)
+static void swap_read_folio_fs(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug)
{
- struct swap_info_struct *sis = __swap_entry_to_info(folio->swap);
struct swap_iocb *sio = NULL;
loff_t pos = swap_dev_pos(folio->swap);
@@ -571,8 +553,9 @@ static void swap_read_folio_fs(struct folio *folio, struct swap_iocb **plug)
*plug = sio;
}
-static void swap_read_folio_bdev_sync(struct folio *folio,
- struct swap_info_struct *sis)
+static void swap_read_folio_bdev_sync(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug)
{
struct bio_vec bv;
struct bio bio;
@@ -593,8 +576,9 @@ static void swap_read_folio_bdev_sync(struct folio *folio,
put_task_struct(current);
}
-static void swap_read_folio_bdev_async(struct folio *folio,
- struct swap_info_struct *sis)
+static void swap_read_folio_bdev_async(struct swap_info_struct *sis,
+ struct folio *folio,
+ struct swap_iocb **plug)
{
struct bio *bio;
@@ -608,6 +592,44 @@ static void swap_read_folio_bdev_async(struct folio *folio,
submit_bio(bio);
}
+static const struct swap_ops bdev_fs_swap_ops = {
+ .read_folio = swap_read_folio_fs,
+ .write_folio = swap_writepage_fs,
+};
+
+static const struct swap_ops bdev_sync_swap_ops = {
+ .read_folio = swap_read_folio_bdev_sync,
+ .write_folio = swap_writepage_bdev_sync,
+};
+
+static const struct swap_ops bdev_async_swap_ops = {
+ .read_folio = swap_read_folio_bdev_async,
+ .write_folio = swap_writepage_bdev_async,
+};
+
+int init_swap_ops(struct swap_info_struct *sis)
+{
+ /*
+ * ->flags can be updated non-atomically, but that will
+ * never affect SWP_FS_OPS, so the data_race is safe.
+ */
+ if (data_race(sis->flags & SWP_FS_OPS))
+ sis->ops = &bdev_fs_swap_ops;
+ /*
+ * ->flags can be updated non-atomically, but that will
+ * never affect SWP_SYNCHRONOUS_IO, so the data_race is safe.
+ */
+ else if (data_race(sis->flags & SWP_SYNCHRONOUS_IO))
+ sis->ops = &bdev_sync_swap_ops;
+ else
+ sis->ops = &bdev_async_swap_ops;
+
+ if (!sis->ops || !sis->ops->read_folio || !sis->ops->write_folio)
+ return -EINVAL;
+
+ return 0;
+}
+
void swap_read_folio(struct folio *folio, struct swap_iocb **plug)
{
struct swap_info_struct *sis = __swap_entry_to_info(folio->swap);
@@ -642,13 +664,7 @@ void swap_read_folio(struct folio *folio, struct swap_iocb **plug)
/* We have to read from slower devices. Increase zswap protection. */
zswap_folio_swapin(folio);
- if (data_race(sis->flags & SWP_FS_OPS)) {
- swap_read_folio_fs(folio, plug);
- } else if (synchronous) {
- swap_read_folio_bdev_sync(folio, sis);
- } else {
- swap_read_folio_bdev_async(folio, sis);
- }
+ sis->ops->read_folio(sis, folio, plug);
finish:
if (workingset) {
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 4840fd40f36f..8c42632e6765 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -3780,6 +3780,15 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
goto free_swap_zswap;
}
+ /*
+ * init_swap_ops() sets si->ops based on flags. It does not need
+ * swapon_mutex, and must complete before enable_swap_info()
+ * exposes the device.
+ */
+ error = init_swap_ops(si);
+ if (error)
+ goto bad_swap_unlock_inode;
+
mutex_lock(&swapon_mutex);
prio = DEF_SWAP_PRIO;
if (swap_flags & SWAP_FLAG_PREFER)
diff --git a/mm/zswap.c b/mm/zswap.c
index 4b5149173b0e..192401f46de4 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1054,7 +1054,7 @@ static int zswap_writeback_entry(struct zswap_entry *entry,
folio_set_reclaim(folio);
/* start writeback */
- __swap_writepage(folio, NULL);
+ si->ops->write_folio(si, folio, NULL);
out:
if (ret && ret != -EEXIST) {
--
2.52.0
next prev parent reply other threads:[~2026-05-12 10:42 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 10:41 [PATCH v6 0/3] mm/swap: use swap_ops to register swap device's methods Baoquan He
2026-05-12 10:41 ` [PATCH v6 1/3] mm/swap: rename mm/page_io.c to mm/swap_io.c Baoquan He
2026-05-13 0:27 ` Chris Li
2026-05-13 5:21 ` Christoph Hellwig
2026-05-13 7:10 ` Baoquan He
2026-05-12 10:42 ` Baoquan He [this message]
2026-05-12 17:53 ` [PATCH v6 2/3] mm/swap: use swap_ops to register swap device's methods Kairui Song
2026-05-14 2:06 ` Baoquan He
2026-05-14 6:41 ` Kairui Song
2026-05-14 8:40 ` Baoquan He
2026-05-13 5:32 ` Christoph Hellwig
2026-05-13 15:33 ` Baoquan He
2026-05-15 6:30 ` Christoph Hellwig
2026-05-15 21:31 ` Chris Li
2026-05-13 5:45 ` Christoph Hellwig
2026-05-13 15:38 ` Baoquan He
2026-05-12 10:42 ` [PATCH v6 3/3] mm/swap_io.c: rename swap_writepage_* to swap_write_folio_* Baoquan He
2026-05-12 17:54 ` Kairui Song
2026-05-13 0:31 ` Chris Li
2026-05-13 5:36 ` Christoph Hellwig
2026-05-13 15:44 ` 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=20260512104201.716213-3-baoquan.he@linux.dev \
--to=baoquan.he@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=chrisl@kernel.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=usama.arif@linux.dev \
--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.