summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2026-07-23 07:46:06 +0200
committerAndrew Morton <akpm@linux-foundation.org>2026-08-24 18:43:20 -0700
commit22779ae8175aad7c04827db44e934e53bf2bd2d4 (patch)
treeb8c9c8cd5ae127cd6f0c77ea461fb1677ca17d5c /fs
parent52d85ca90c7db41f8cfc72e5fe7dc62c2f983032 (diff)
mm/swap: move swap_ops into file systems for file system-based swap
Currently swap to and from file systems goes through two indirect calls between the swap ops and the swap_rw method. Reduce this by directly providing the swap_ops from the file system. For this refactor swap_fs_submit into a swap_fs_prepare_rw helper that initializes the iov_iter on the callers stack so that file systems can call it directly, and use that to initialize file system specific ops in the NFS and SMB clients, which then get passed to swap_fs_activate. Link: https://lore.kernel.org/20260723054622.3460249-4-hch@lst.de Signed-off-by: Christoph Hellwig <hch@lst.de> Acked-by: Chris Li <chrisl@kernel.org> Cc: Baoquan He <baoquan.he@linux.dev> Cc: Kairui Song <kasong@tencent.com> Cc: Kairui Song <ryncsn@gmail.com> Cc: Kemeng Shi <shikemeng@huaweicloud.com> Cc: Nhat Pham <nphamcs@gmail.com> Cc: Steve French <sfrench@samba.org> Cc: Usama Arif <usama.arif@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/direct.c20
-rw-r--r--fs/nfs/file.c42
-rw-r--r--fs/smb/client/file.c63
3 files changed, 77 insertions, 48 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index e626c72495e6..ccafdc1ce64d 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -145,26 +145,6 @@ static void nfs_direct_file_adjust_size_locked(struct inode *inode,
}
}
-/**
- * nfs_swap_rw - NFS address space operation for swap I/O
- * @iocb: target I/O control block
- * @iter: I/O buffer
- *
- * Perform IO to the swap-file. This is much like direct IO.
- */
-int nfs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
-{
- ssize_t ret;
-
- if (iov_iter_rw(iter) == READ)
- ret = nfs_file_direct_read(iocb, iter, true);
- else
- ret = nfs_file_direct_write(iocb, iter, true);
- if (ret < 0)
- return ret;
- return 0;
-}
-
static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
{
unsigned int i;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 851d93a09988..e1bdd10b35f1 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -29,9 +29,8 @@
#include <linux/pagemap.h>
#include <linux/gfp.h>
#include <linux/rmap.h>
-#include <linux/swap.h>
#include <linux/compaction.h>
-
+#include <linux/swap_ops.h>
#include <linux/uaccess.h>
#include <linux/filelock.h>
@@ -575,6 +574,38 @@ static int nfs_launder_folio(struct folio *folio)
return ret;
}
+#ifdef CONFIG_SWAP
+static void nfs_swap_submit_write(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, WRITE, &iter);
+ ret = nfs_file_direct_write(&sio->iocb, &iter, true);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static void nfs_swap_submit_read(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, READ, &iter);
+ ret = nfs_file_direct_read(&sio->iocb, &iter, true);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static const struct swap_ops nfs_swap_ops = {
+ .flags = SWAP_OPS_F_REQUIRE_NOFS,
+ .submit_write = nfs_swap_submit_write,
+ .submit_read = nfs_swap_submit_read,
+ .can_merge = swap_fs_can_merge,
+};
+
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
sector_t *span)
{
@@ -597,7 +628,7 @@ static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
ret = rpc_clnt_swap_activate(clnt);
if (ret)
return ret;
- ret = swap_fs_activate(sis);
+ ret = swap_fs_activate(sis, &nfs_swap_ops);
if (ret < 0) {
rpc_clnt_swap_deactivate(clnt);
return ret;
@@ -620,6 +651,10 @@ static void nfs_swap_deactivate(struct file *file)
if (cl->rpc_ops->disable_swap)
cl->rpc_ops->disable_swap(file_inode(file));
}
+#else
+#define nfs_swap_activate NULL
+#define nfs_swap_deactivate NULL
+#endif /* CONFIG_SWAP */
const struct address_space_operations nfs_file_aops = {
.read_folio = nfs_read_folio,
@@ -636,7 +671,6 @@ const struct address_space_operations nfs_file_aops = {
.error_remove_folio = generic_error_remove_folio,
.swap_activate = nfs_swap_activate,
.swap_deactivate = nfs_swap_deactivate,
- .swap_rw = nfs_swap_rw,
};
/*
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 7f2924ce2881..ead69232ac1c 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -20,7 +20,7 @@
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/slab.h>
-#include <linux/swap.h>
+#include <linux/swap_ops.h>
#include <linux/mm.h>
#include <asm/div64.h>
#include "cifsfs.h"
@@ -3410,6 +3410,38 @@ out:
cifs_done_oplock_break(cinode);
}
+#ifdef CONFIG_SWAP
+static void cifs_swap_submit_write(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, WRITE, &iter);
+ ret = netfs_unbuffered_write_iter_locked(&sio->iocb, &iter, NULL);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static void cifs_swap_submit_read(struct swap_io_ctx *ctx)
+{
+ struct swap_iocb *sio = ctx->sio;
+ struct iov_iter iter;
+ int ret;
+
+ swap_fs_prepare_rw(ctx, READ, &iter);
+ ret = netfs_unbuffered_read_iter_locked(&sio->iocb, &iter);
+ if (ret != -EIOCBQUEUED)
+ sio->iocb.ki_complete(&sio->iocb, ret);
+}
+
+static const struct swap_ops cifs_swap_ops = {
+ .flags = SWAP_OPS_F_REQUIRE_NOFS,
+ .submit_write = cifs_swap_submit_write,
+ .submit_read = cifs_swap_submit_read,
+ .can_merge = swap_fs_can_merge,
+};
+
static int cifs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
@@ -3420,7 +3452,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
cifs_dbg(FYI, "swap activate\n");
- if (!swap_file->f_mapping->a_ops->swap_rw)
+ if (swap_file->f_mapping->a_ops != &cifs_addr_ops)
/* Cannot support swap */
return -EINVAL;
@@ -3451,7 +3483,7 @@ static int cifs_swap_activate(struct swap_info_struct *sis,
* but we could add call to grab a byte range lock to prevent others
* from reading or writing the file
*/
- return swap_fs_activate(sis);
+ return swap_fs_activate(sis, &cifs_swap_ops);
}
static void cifs_swap_deactivate(struct file *file)
@@ -3467,26 +3499,10 @@ static void cifs_swap_deactivate(struct file *file)
/* do we need to unpin (or unlock) the file */
}
-
-/**
- * cifs_swap_rw - SMB3 address space operation for swap I/O
- * @iocb: target I/O control block
- * @iter: I/O buffer
- *
- * Perform IO to the swap-file. This is much like direct IO.
- */
-static int cifs_swap_rw(struct kiocb *iocb, struct iov_iter *iter)
-{
- ssize_t ret;
-
- if (iov_iter_rw(iter) == READ)
- ret = netfs_unbuffered_read_iter_locked(iocb, iter);
- else
- ret = netfs_unbuffered_write_iter_locked(iocb, iter, NULL);
- if (ret < 0)
- return ret;
- return 0;
-}
+#else
+#define cifs_swap_activate NULL
+#define cifs_swap_deactivate NULL
+#endif /* CONFIG_SWAP */
const struct address_space_operations cifs_addr_ops = {
.read_folio = netfs_read_folio,
@@ -3503,7 +3519,6 @@ const struct address_space_operations cifs_addr_ops = {
*/
.swap_activate = cifs_swap_activate,
.swap_deactivate = cifs_swap_deactivate,
- .swap_rw = cifs_swap_rw,
};
/*