[PATCH v7 3/3] mm/swap: add unplug callback to swap_ops to fix leaky abstraction
From: Baoquan He
Date: Thu May 14 2026 - 22:00:46 EST
When swap_ops was introduced, the FS-swap batch submission remained
as a standalone swap_write_unplug() that directly called
mapping->a_ops->swap_rw(). This meant callers still had implicit
knowledge of filesystem internals rather than going through the
swap_ops abstraction.
Fix this by adding an unplug callback to struct swap_ops.
Each ops table provides its own implementation:
- bdev_fs_swap_ops uses the existing FS batch-submission logic
- bdev_sync/bdev_async_swap_ops leave it NULL since block-layer
plugging handles their I/O
The swap_iocb now carries a pointer to its ops table so that
swap_write_unplug() can dispatch through the callback without
the caller needing to know the swap device type.
Signed-off-by: Baoquan He <baoquan.he@xxxxxxxxx>
---
mm/page_io.c | 11 ++++++++++-
mm/swap.h | 1 +
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/mm/page_io.c b/mm/page_io.c
index 38b94c560c37..2c36d261ad98 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -329,6 +329,7 @@ static void bio_associate_blkg_from_page(struct bio *bio, struct folio *folio)
struct swap_iocb {
struct kiocb iocb;
+ const struct swap_ops *ops;
struct bio_vec bvec[SWAP_CLUSTER_MAX];
int pages;
int len;
@@ -401,6 +402,7 @@ static void swap_fs_write_folio(struct swap_info_struct *sis,
init_sync_kiocb(&sio->iocb, swap_file);
sio->iocb.ki_complete = sio_write_complete;
sio->iocb.ki_pos = pos;
+ sio->ops = sis->ops;
sio->pages = 0;
sio->len = 0;
}
@@ -454,7 +456,7 @@ static void swap_bdev_async_write_folio(struct swap_info_struct *sis,
submit_bio(bio);
}
-void swap_write_unplug(struct swap_iocb *sio)
+static void swap_fs_write_folio_unplug(struct swap_iocb *sio)
{
struct iov_iter from;
struct address_space *mapping = sio->iocb.ki_filp->f_mapping;
@@ -466,6 +468,12 @@ void swap_write_unplug(struct swap_iocb *sio)
sio_write_complete(&sio->iocb, ret);
}
+void swap_write_unplug(struct swap_iocb *sio)
+{
+ if (sio->ops && sio->ops->unplug)
+ sio->ops->unplug(sio);
+}
+
static void sio_read_complete(struct kiocb *iocb, long ret)
{
struct swap_iocb *sio = container_of(iocb, struct swap_iocb, iocb);
@@ -599,6 +607,7 @@ static void swap_bdev_async_read_folio(struct swap_info_struct *sis,
static const struct swap_ops bdev_fs_swap_ops = {
.read_folio = swap_fs_read_folio,
.write_folio = swap_fs_write_folio,
+ .unplug = swap_fs_write_folio_unplug,
};
static const struct swap_ops bdev_sync_swap_ops = {
diff --git a/mm/swap.h b/mm/swap.h
index 8d4375f91632..67e3b1617146 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -224,6 +224,7 @@ struct swap_ops {
void (*write_folio)(struct swap_info_struct *sis,
struct folio *folio,
struct swap_iocb **plug);
+ void (*unplug)(struct swap_iocb *sio);
};
int init_swap_ops(struct swap_info_struct *sis);
void swap_read_folio(struct folio *folio, struct swap_iocb **plug);
--
2.52.0