* [PATCH v2 1/2] nilfs2: add iomap operations for direct I/O
2026-08-08 0:35 [PATCH v2 0/2] nilfs2: eliminate blockdev_direct_IO() call Viacheslav Dubeyko
@ 2026-08-08 0:35 ` Viacheslav Dubeyko
2026-08-08 0:35 ` [PATCH v2 2/2] nilfs2: switch O_DIRECT to iomap based operations Viacheslav Dubeyko
1 sibling, 0 replies; 5+ messages in thread
From: Viacheslav Dubeyko @ 2026-08-08 0:35 UTC (permalink / raw)
To: konishi.ryusuke, hch
Cc: linux-nilfs, linux-fsdevel, lilinmao, Viacheslav Dubeyko
The patch adds iomap.h with declaration of iomaps
operations and iomap.c with implementation of
nilfs_iomap_begin(). This method is based on calling
the nilfs_bmap_lookup_contig() method. It resolves
the file's block-mapping to find the sequence of
physically-contiguous blocks starting at starting
block. Offsets at or beyond i_size are reported
as a hole. For offsets within i_size, the requested
length is first clamped to the block-aligned end of the file.
Both the hole and mapped cases then report iomap->offset
aligned to the containing block (blkoff << i_blkbits)
rather than the raw request offset.
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Co-developed-by: Linmao Li <lilinmao@kylinos.cn>
Signed-off-by: Linmao Li <lilinmao@kylinos.cn>
cc: Christoph Hellwig <hch@lst.de>
cc: Ryusuke Konishi <konishi.ryusuke@gmail.com>
cc: linux-nilfs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
fs/nilfs2/Makefile | 2 +-
fs/nilfs2/iomap.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++
fs/nilfs2/iomap.h | 13 +++++++++
3 files changed, 86 insertions(+), 1 deletion(-)
create mode 100644 fs/nilfs2/iomap.c
create mode 100644 fs/nilfs2/iomap.h
diff --git a/fs/nilfs2/Makefile b/fs/nilfs2/Makefile
index 43b60b8a4d07..516e6b85a03c 100644
--- a/fs/nilfs2/Makefile
+++ b/fs/nilfs2/Makefile
@@ -3,4 +3,4 @@ obj-$(CONFIG_NILFS2_FS) += nilfs2.o
nilfs2-y := inode.o file.o dir.o super.o namei.o page.o mdt.o \
btnode.o bmap.o btree.o direct.o dat.o recovery.o \
the_nilfs.o segbuf.o segment.o cpfile.o sufile.o \
- ifile.o alloc.o gcinode.o ioctl.o sysfs.o
+ ifile.o alloc.o gcinode.o ioctl.o sysfs.o iomap.o
diff --git a/fs/nilfs2/iomap.c b/fs/nilfs2/iomap.c
new file mode 100644
index 000000000000..e130ed63abd9
--- /dev/null
+++ b/fs/nilfs2/iomap.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NILFS iomap support implementation.
+ *
+ * Written by Viacheslav Dubeyko.
+ */
+
+#include <linux/iomap.h>
+#include <linux/pagemap.h>
+#include "nilfs.h"
+#include "mdt.h"
+#include "iomap.h"
+
+static int nilfs_iomap_begin(struct inode *inode, loff_t offset,
+ loff_t length, unsigned int flags,
+ struct iomap *iomap, struct iomap *srcmap)
+{
+ struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
+ struct nilfs_inode_info *ii = NILFS_I(inode);
+ sector_t blkoff = offset >> inode->i_blkbits;
+ loff_t iomap_offset = (loff_t)blkoff << inode->i_blkbits;
+ unsigned int maxblocks;
+ __u64 blknum = 0;
+ int ret;
+
+ /* Completely beyond EOF. Treat as hole */
+ if (i_size_read(inode) <= offset) {
+ iomap->type = IOMAP_HOLE;
+ iomap->addr = IOMAP_NULL_ADDR;
+ iomap->offset = offset;
+ iomap->length = length;
+ return 0;
+ }
+
+ /* Clamp length if the requested range goes beyond i_size */
+ if (offset + length > i_size_read(inode)) {
+ loff_t i_size = i_size_read(inode);
+ unsigned int blocksize = i_blocksize(inode);
+
+ length = round_up(i_size, blocksize) - offset;
+ }
+
+ maxblocks = min_t(loff_t, length >> inode->i_blkbits, INT_MAX);
+ if (maxblocks == 0)
+ maxblocks = 1;
+
+ down_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
+ ret = nilfs_bmap_lookup_contig(ii->i_bmap, blkoff, &blknum, maxblocks);
+ up_read(&NILFS_MDT(nilfs->ns_dat)->mi_sem);
+
+ if (ret == -ENOENT) {
+ iomap->type = IOMAP_HOLE;
+ iomap->addr = IOMAP_NULL_ADDR;
+ iomap->offset = iomap_offset;
+ iomap->length = i_blocksize(inode);
+ return 0;
+ } else if (ret < 0)
+ return ret;
+
+ iomap->bdev = inode->i_sb->s_bdev;
+ iomap->offset = iomap_offset;
+ iomap->length = (loff_t)ret << inode->i_blkbits;
+ iomap->addr = (loff_t)blknum << inode->i_blkbits;
+ iomap->type = IOMAP_MAPPED;
+ iomap->flags = IOMAP_F_MERGED;
+
+ return 0;
+}
+
+const struct iomap_ops nilfs_iomap_ops = {
+ .iomap_begin = nilfs_iomap_begin,
+};
diff --git a/fs/nilfs2/iomap.h b/fs/nilfs2/iomap.h
new file mode 100644
index 000000000000..adef3e22346d
--- /dev/null
+++ b/fs/nilfs2/iomap.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NILFS iomap support declarations.
+ *
+ * Written by Viacheslav Dubeyko.
+ */
+
+#ifndef _NILFS_IOMAP_H
+#define _NILFS_IOMAP_H
+
+extern const struct iomap_ops nilfs_iomap_ops;
+
+#endif /* _NILFS_IOMAP_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 2/2] nilfs2: switch O_DIRECT to iomap based operations
2026-08-08 0:35 [PATCH v2 0/2] nilfs2: eliminate blockdev_direct_IO() call Viacheslav Dubeyko
2026-08-08 0:35 ` [PATCH v2 1/2] nilfs2: add iomap operations for direct I/O Viacheslav Dubeyko
@ 2026-08-08 0:35 ` Viacheslav Dubeyko
2026-08-08 23:51 ` Ryusuke Konishi
1 sibling, 1 reply; 5+ messages in thread
From: Viacheslav Dubeyko @ 2026-08-08 0:35 UTC (permalink / raw)
To: konishi.ryusuke, hch
Cc: linux-nilfs, linux-fsdevel, lilinmao, Viacheslav Dubeyko
The patch eliminates blockdev_direct_IO() from
nilfs2 entirely:
- nilfs_file_open() now sets FMODE_CAN_ODIRECT explicitly, since
permission to open the file O_DIRECT was previously implied by
aops->direct_IO being non-NULL.
- nilfs_file_read_iter() dispatches O_DIRECT reads to iomap_dio_rw()
using nilfs_iomap_ops; everything else still goes through
generic_file_read_iter() as before.
- nilfs_file_write_iter() strips IOCB_DIRECT and falls through to
generic_file_write_iter()'s ordinary buffered path. NILFS2 cannot
perform true direct I/O writes: new blocks are delay-allocated and
only given a real disk address by the segment constructor, which
works on buffer_head lists, not iomap. This reproduces today's
actual behavior: the old nilfs_direct_IO() already just returned 0
for WRITE.
- nilfs_direct_IO() and the .direct_IO callback on nilfs_aops are
removed.
- drop the unnecessary "select LEGACY_DIRECT_IO" from Kconfig
in favor of "select FS_IOMAP".
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
cc: Christoph Hellwig <hch@lst.de>
cc: Ryusuke Konishi <konishi.ryusuke@gmail.com>
cc: linux-nilfs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
fs/nilfs2/Kconfig | 2 +-
fs/nilfs2/file.c | 40 +++++++++++++++++++++++++++++++++++++---
fs/nilfs2/inode.c | 13 -------------
3 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/fs/nilfs2/Kconfig b/fs/nilfs2/Kconfig
index 7dae168e346e..0a5ace60e6ab 100644
--- a/fs/nilfs2/Kconfig
+++ b/fs/nilfs2/Kconfig
@@ -3,7 +3,7 @@ config NILFS2_FS
tristate "NILFS2 file system support"
select BUFFER_HEAD
select CRC32
- select LEGACY_DIRECT_IO
+ select FS_IOMAP
help
NILFS2 is a log-structured file system (LFS) supporting continuous
snapshotting. In addition to versioning capability of the entire
diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c
index f93b68c4877c..ad2e87c049c9 100644
--- a/fs/nilfs2/file.c
+++ b/fs/nilfs2/file.c
@@ -10,9 +10,12 @@
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/mm.h>
+#include <linux/uio.h>
+#include <linux/iomap.h>
#include <linux/writeback.h>
#include "nilfs.h"
#include "segment.h"
+#include "iomap.h"
int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
@@ -133,20 +136,51 @@ static int nilfs_file_mmap_prepare(struct vm_area_desc *desc)
return 0;
}
+static int nilfs_file_open(struct inode *inode, struct file *file)
+{
+ file->f_mode |= FMODE_CAN_ODIRECT;
+ return generic_file_open(inode, file);
+}
+
+static ssize_t nilfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ if (iocb->ki_flags & IOCB_DIRECT) {
+ return iomap_dio_rw(iocb, to, &nilfs_iomap_ops,
+ NULL, 0, NULL, 0);
+ } else
+ return generic_file_read_iter(iocb, to);
+}
+
+static ssize_t nilfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ /*
+ * NILFS2 cannot perform true direct I/O writes: new blocks are
+ * delay-allocated and are only given a real disk address when
+ * the segment constructor writes them out as part of a log,
+ * which works directly on buffer_head lists rather than
+ * through iomap. Fall back to the ordinary buffered write path
+ * for O_DIRECT writes.
+ */
+ if (iocb->ki_flags & IOCB_DIRECT)
+ iocb->ki_flags &= ~IOCB_DIRECT;
+
+ return generic_file_write_iter(iocb, from);
+}
+
/*
* We have mostly NULL's here: the current defaults are ok for
* the nilfs filesystem.
*/
const struct file_operations nilfs_file_operations = {
.llseek = generic_file_llseek,
- .read_iter = generic_file_read_iter,
- .write_iter = generic_file_write_iter,
+ .read_iter = nilfs_file_read_iter,
+ .write_iter = nilfs_file_write_iter,
.unlocked_ioctl = nilfs_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = nilfs_compat_ioctl,
#endif /* CONFIG_COMPAT */
.mmap_prepare = nilfs_file_mmap_prepare,
- .open = generic_file_open,
+ .open = nilfs_file_open,
/* .release = nilfs_release_file, */
.fsync = nilfs_sync_file,
.splice_read = filemap_splice_read,
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 51f7e125a311..f4a9d9ea9c3f 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -257,18 +257,6 @@ static int nilfs_write_end(const struct kiocb *iocb,
return err ? : copied;
}
-static ssize_t
-nilfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
-{
- struct inode *inode = file_inode(iocb->ki_filp);
-
- if (iov_iter_rw(iter) == WRITE)
- return 0;
-
- /* Needs synchronization with the cleaner */
- return blockdev_direct_IO(iocb, inode, iter, nilfs_get_block);
-}
-
const struct address_space_operations nilfs_aops = {
.read_folio = nilfs_read_folio,
.writepages = nilfs_writepages,
@@ -277,7 +265,6 @@ const struct address_space_operations nilfs_aops = {
.write_begin = nilfs_write_begin,
.write_end = nilfs_write_end,
.invalidate_folio = block_invalidate_folio,
- .direct_IO = nilfs_direct_IO,
.migrate_folio = buffer_migrate_folio_norefs,
.is_partially_uptodate = block_is_partially_uptodate,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread