All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap
@ 2026-07-24 22:27 Viacheslav Dubeyko
  2026-07-24 22:27 ` [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads Viacheslav Dubeyko
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-24 22:27 UTC (permalink / raw)
  To: konishi.ryusuke, hch; +Cc: linux-nilfs, linux-fsdevel, Viacheslav Dubeyko

This series removes NILFS2 using of blockdev_direct_IO() by
converting the O_DIRECT read path to iomap. NILFS2 is
a log-structured, copy-on-write filesystem, which makes it
a poor fit for a full iomap conversion. Newly allocated blocks
are delay-allocated by nilfs_get_block() and only receive a real
disk address when the segment constructor writes them out
as part of a log. The segment constructor walks buffer_head lists
directly and is not integrated with the generic address_space
writeback path. First patch adds a new, read-only nilfs_iomap_ops
wrapping the existing nilfs_bmap_lookup_contig() lookup.
The second patch wires that ops table into O_DIRECT reads via
iomap_dio_rw(), sets FMODE_CAN_ODIRECT explicitly on open, and
removes nilfs_direct_IO() and the .direct_IO callback entirely.
O_DIRECT writes are explicitly downgraded to the ordinary buffered
write path in nilfs_file_write_iter().

Viacheslav Dubeyko (2):
  nilfs2: add iomap operations for direct I/O reads
  nilfs2: switch O_DIRECT reads to iomap

 fs/nilfs2/Kconfig  |  2 +-
 fs/nilfs2/Makefile |  2 +-
 fs/nilfs2/file.c   | 40 ++++++++++++++++++++++++--
 fs/nilfs2/inode.c  | 13 ---------
 fs/nilfs2/iomap.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/nilfs2/iomap.h  | 13 +++++++++
 6 files changed, 123 insertions(+), 18 deletions(-)
 create mode 100644 fs/nilfs2/iomap.c
 create mode 100644 fs/nilfs2/iomap.h

-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads
  2026-07-24 22:27 [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap Viacheslav Dubeyko
@ 2026-07-24 22:27 ` Viacheslav Dubeyko
  2026-07-25 22:33   ` Ryusuke Konishi
  2026-07-24 22:27 ` [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap Viacheslav Dubeyko
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-24 22:27 UTC (permalink / raw)
  To: konishi.ryusuke, hch; +Cc: linux-nilfs, linux-fsdevel, Viacheslav Dubeyko

Add iomap.c and iomap.h with a read-only nilfs_iomap_ops,
wrapping the existing nilfs_bmap_lookup_contig() lookup
to report a mapped range, a real hole, or an EOF-clamped
range to iomap core.

NILFS2 is a log-structured, copy-on-write filesystem:
newly allocated blocks are only given a real disk address
when the segment constructor writes them out as part of a log,
which walks buffer_head lists directly and is not integrated
with the generic address_space writeback path. Because of that,
only the read-only side of the mapping is added - buffered writes,
writeback, and mmap's ->page_mkwrite() will stay on the existing
buffer_head based path (nilfs_get_block(), nilfs_write_begin/end(),
nilfs_writepages(), nilfs_dirty_folio(), block_page_mkwrite()).

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/Makefile |  2 +-
 fs/nilfs2/iomap.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/nilfs2/iomap.h  | 13 +++++++++
 3 files changed, 85 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..3ae3bf6ed368
--- /dev/null
+++ b/fs/nilfs2/iomap.c
@@ -0,0 +1,71 @@
+// 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;
+	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 = offset;
+		iomap->length = min_t(loff_t, length, i_blocksize(inode));
+		return 0;
+	} else if (ret < 0)
+		return ret;
+
+	iomap->bdev = inode->i_sb->s_bdev;
+	iomap->offset = offset;
+	iomap->length = min_t(loff_t, 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] 11+ messages in thread

* [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap
  2026-07-24 22:27 [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap Viacheslav Dubeyko
  2026-07-24 22:27 ` [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads Viacheslav Dubeyko
@ 2026-07-24 22:27 ` Viacheslav Dubeyko
  2026-07-25 23:00   ` Ryusuke Konishi
  2026-07-27  9:43 ` [PATCH 0/2] nilfs2: convert " Christian Brauner
  2026-07-28  3:54 ` Christoph Hellwig
  3 siblings, 1 reply; 11+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-24 22:27 UTC (permalink / raw)
  To: konishi.ryusuke, hch; +Cc: linux-nilfs, linux-fsdevel, Viacheslav Dubeyko

Wire the read-only nilfs_iomap_ops added in the previous patch into
the O_DIRECT read path, and eliminate 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] 11+ messages in thread

* Re: [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads
  2026-07-24 22:27 ` [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads Viacheslav Dubeyko
@ 2026-07-25 22:33   ` Ryusuke Konishi
  2026-07-28  0:46     ` Viacheslav Dubeyko
  0 siblings, 1 reply; 11+ messages in thread
From: Ryusuke Konishi @ 2026-07-25 22:33 UTC (permalink / raw)
  To: Viacheslav Dubeyko; +Cc: hch, linux-nilfs, linux-fsdevel

Hi Viacheslav,

On Sat, Jul 25, 2026 at 7:28 AM Viacheslav Dubeyko wrote:
>
> Add iomap.c and iomap.h with a read-only nilfs_iomap_ops,
> wrapping the existing nilfs_bmap_lookup_contig() lookup
> to report a mapped range, a real hole, or an EOF-clamped
> range to iomap core.
>
> NILFS2 is a log-structured, copy-on-write filesystem:
> newly allocated blocks are only given a real disk address
> when the segment constructor writes them out as part of a log,
> which walks buffer_head lists directly and is not integrated
> with the generic address_space writeback path. Because of that,
> only the read-only side of the mapping is added - buffered writes,
> writeback, and mmap's ->page_mkwrite() will stay on the existing
> buffer_head based path (nilfs_get_block(), nilfs_write_begin/end(),
> nilfs_writepages(), nilfs_dirty_folio(), block_page_mkwrite()).
>
> 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/Makefile |  2 +-
>  fs/nilfs2/iomap.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++
>  fs/nilfs2/iomap.h  | 13 +++++++++
>  3 files changed, 85 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..3ae3bf6ed368
> --- /dev/null
> +++ b/fs/nilfs2/iomap.c
> @@ -0,0 +1,71 @@
> +// 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;
> +       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 = offset;
> +               iomap->length = min_t(loff_t, length, i_blocksize(inode));

After encountering a hole, I think we should use nilfs_bmap_seek_key()
-- as nilfs_mdt_find_block() does for metadata files -- to efficiently
determine its length.

However, if you are short on time, I am fine with the current approach as well.

Acked-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>

Thanks,
Ryusuke Konishi

> +               return 0;
> +       } else if (ret < 0)
> +               return ret;
> +
> +       iomap->bdev = inode->i_sb->s_bdev;
> +       iomap->offset = offset;
> +       iomap->length = min_t(loff_t, 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	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap
  2026-07-24 22:27 ` [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap Viacheslav Dubeyko
@ 2026-07-25 23:00   ` Ryusuke Konishi
  0 siblings, 0 replies; 11+ messages in thread
From: Ryusuke Konishi @ 2026-07-25 23:00 UTC (permalink / raw)
  To: Viacheslav Dubeyko; +Cc: hch, linux-nilfs, linux-fsdevel

On Sat, Jul 25, 2026 at 7:28 AM Viacheslav Dubeyko  wrote:
>
> Wire the read-only nilfs_iomap_ops added in the previous patch into
> the O_DIRECT read path, and eliminate 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

The conversion looks good to me.
(Note: I haven't exhaustively checked for any iomap-specific caveats.)

I verified the build and basic operations in several local
environments, and the changes have been working without any critical
issues so far.

Acked-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>

Thanks,
Ryusuke Konishi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap
  2026-07-24 22:27 [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap Viacheslav Dubeyko
  2026-07-24 22:27 ` [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads Viacheslav Dubeyko
  2026-07-24 22:27 ` [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap Viacheslav Dubeyko
@ 2026-07-27  9:43 ` Christian Brauner
  2026-07-28  3:54 ` Christoph Hellwig
  3 siblings, 0 replies; 11+ messages in thread
From: Christian Brauner @ 2026-07-27  9:43 UTC (permalink / raw)
  To: konishi.ryusuke, hch, Viacheslav Dubeyko; +Cc: linux-nilfs, linux-fsdevel

On Fri, 24 Jul 2026 15:27:43 -0700, Viacheslav Dubeyko wrote:
> nilfs2: convert O_DIRECT reads to iomap
> 
> This series removes NILFS2 using of blockdev_direct_IO() by
> converting the O_DIRECT read path to iomap. NILFS2 is
> a log-structured, copy-on-write filesystem, which makes it
> a poor fit for a full iomap conversion. Newly allocated blocks
> are delay-allocated by nilfs_get_block() and only receive a real
> disk address when the segment constructor writes them out
> as part of a log. The segment constructor walks buffer_head lists
> directly and is not integrated with the generic address_space
> writeback path. First patch adds a new, read-only nilfs_iomap_ops
> wrapping the existing nilfs_bmap_lookup_contig() lookup.
> The second patch wires that ops table into O_DIRECT reads via
> iomap_dio_rw(), sets FMODE_CAN_ODIRECT explicitly on open, and
> removes nilfs_direct_IO() and the .direct_IO callback entirely.
> O_DIRECT writes are explicitly downgraded to the ordinary buffered
> write path in nilfs_file_write_iter().
> 
> [...]

Applied to the vfs-7.3.nilfs2 branch of the vfs/vfs.git tree.
Patches in the vfs-7.3.nilfs2 branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.3.nilfs2

[1/2] nilfs2: add iomap operations for direct I/O reads
      https://git.kernel.org/vfs/vfs/c/e7b2c51c3fed
[2/2] nilfs2: switch O_DIRECT reads to iomap
      https://git.kernel.org/vfs/vfs/c/b924d8d4e54f


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads
  2026-07-25 22:33   ` Ryusuke Konishi
@ 2026-07-28  0:46     ` Viacheslav Dubeyko
  2026-07-28  3:49       ` Ryusuke Konishi
  0 siblings, 1 reply; 11+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-28  0:46 UTC (permalink / raw)
  To: Ryusuke Konishi; +Cc: hch, linux-nilfs, linux-fsdevel

Hi Ryusuke,

On Sun, 2026-07-26 at 07:33 +0900, Ryusuke Konishi wrote:
> Hi Viacheslav,
> 
> On Sat, Jul 25, 2026 at 7:28 AM Viacheslav Dubeyko wrote:
> > 
> > Add iomap.c and iomap.h with a read-only nilfs_iomap_ops,
> > wrapping the existing nilfs_bmap_lookup_contig() lookup
> > to report a mapped range, a real hole, or an EOF-clamped
> > range to iomap core.
> > 
> > NILFS2 is a log-structured, copy-on-write filesystem:
> > newly allocated blocks are only given a real disk address
> > when the segment constructor writes them out as part of a log,
> > which walks buffer_head lists directly and is not integrated
> > with the generic address_space writeback path. Because of that,
> > only the read-only side of the mapping is added - buffered writes,
> > writeback, and mmap's ->page_mkwrite() will stay on the existing
> > buffer_head based path (nilfs_get_block(), nilfs_write_begin/end(),
> > nilfs_writepages(), nilfs_dirty_folio(), block_page_mkwrite()).
> > 
> > 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/Makefile |  2 +-
> >  fs/nilfs2/iomap.c  | 71
> > ++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/nilfs2/iomap.h  | 13 +++++++++
> >  3 files changed, 85 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..3ae3bf6ed368
> > --- /dev/null
> > +++ b/fs/nilfs2/iomap.c
> > @@ -0,0 +1,71 @@
> > +// 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;
> > +       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 = offset;
> > +               iomap->length = min_t(loff_t, length,
> > i_blocksize(inode));
> 
> After encountering a hole, I think we should use
> nilfs_bmap_seek_key()
> -- as nilfs_mdt_find_block() does for metadata files -- to
> efficiently
> determine its length.
> 
> However, if you are short on time, I am fine with the current
> approach as well.

I think we can rework it later. I am starting a new job today and I am
attending FMS 2026 next week. So, I am expecting to be really busy at
minimum two weeks (probably, slightly more). So, let's keep it as TODO.
Please, remind me to rework it later. :)

Thanks,
Slava.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads
  2026-07-28  0:46     ` Viacheslav Dubeyko
@ 2026-07-28  3:49       ` Ryusuke Konishi
  0 siblings, 0 replies; 11+ messages in thread
From: Ryusuke Konishi @ 2026-07-28  3:49 UTC (permalink / raw)
  To: Viacheslav Dubeyko; +Cc: hch, linux-nilfs, linux-fsdevel

On Tue, Jul 28, 2026 at 9:46 AM Viacheslav Dubeyko wrote:
>
> Hi Ryusuke,
>
> On Sun, 2026-07-26 at 07:33 +0900, Ryusuke Konishi wrote:
> > After encountering a hole, I think we should use
> > nilfs_bmap_seek_key()
> > -- as nilfs_mdt_find_block() does for metadata files -- to
> > efficiently
> > determine its length.
> >
> > However, if you are short on time, I am fine with the current
> > approach as well.
>
> I think we can rework it later. I am starting a new job today and I am
> attending FMS 2026 next week. So, I am expecting to be really busy at
> minimum two weeks (probably, slightly more). So, let's keep it as TODO.
> Please, remind me to rework it later. :)
>
> Thanks,
> Slava.

Hi Viacheslav,

Understood.
As I mentioned in my comment, it’s fine to put this on the back burner for now.

Thanks,
Ryusuke Konishi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap
  2026-07-24 22:27 [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap Viacheslav Dubeyko
                   ` (2 preceding siblings ...)
  2026-07-27  9:43 ` [PATCH 0/2] nilfs2: convert " Christian Brauner
@ 2026-07-28  3:54 ` Christoph Hellwig
  2026-07-28 23:19   ` Viacheslav Dubeyko
  3 siblings, 1 reply; 11+ messages in thread
From: Christoph Hellwig @ 2026-07-28  3:54 UTC (permalink / raw)
  To: Viacheslav Dubeyko; +Cc: konishi.ryusuke, hch, linux-nilfs, linux-fsdevel

On Fri, Jul 24, 2026 at 03:27:43PM -0700, Viacheslav Dubeyko wrote:
> This series removes NILFS2 using of blockdev_direct_IO() by
> converting the O_DIRECT read path to iomap.

I think the important part here is that right now there are no
direct I/O writes in nilfs2, not that you only convert the read
path?

> NILFS2 is
> a log-structured, copy-on-write filesystem, which makes it
> a poor fit for a full iomap conversion. Newly allocated blocks
> are delay-allocated by nilfs_get_block() and only receive a real
> disk address when the segment constructor writes them out
> as part of a log.

That's just how zoned xfs and btrfs work.  In fact they only know
the disk address one the write returns, so even later.

Take a look at the IOMAP_F_ANON_WRITE flag used by xfs.  btrfs implements
a similar scheme, but hidden behind a few layers of cruft.

> The segment constructor walks buffer_head lists
> directly and is not integrated with the generic address_space
> writeback path.

This sounds like word-soup to me.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap
  2026-07-28  3:54 ` Christoph Hellwig
@ 2026-07-28 23:19   ` Viacheslav Dubeyko
  2026-07-29  6:28     ` Christoph Hellwig
  0 siblings, 1 reply; 11+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-28 23:19 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: konishi.ryusuke, linux-nilfs, linux-fsdevel

On Tue, 2026-07-28 at 05:54 +0200, Christoph Hellwig wrote:
> On Fri, Jul 24, 2026 at 03:27:43PM -0700, Viacheslav Dubeyko wrote:
> > This series removes NILFS2 using of blockdev_direct_IO() by
> > converting the O_DIRECT read path to iomap.
> 
> I think the important part here is that right now there are no
> direct I/O writes in nilfs2, not that you only convert the read
> path?
> 
> > NILFS2 is
> > a log-structured, copy-on-write filesystem, which makes it
> > a poor fit for a full iomap conversion. Newly allocated blocks
> > are delay-allocated by nilfs_get_block() and only receive a real
> > disk address when the segment constructor writes them out
> > as part of a log.
> 
> That's just how zoned xfs and btrfs work.  In fact they only know
> the disk address one the write returns, so even later.
> 
> Take a look at the IOMAP_F_ANON_WRITE flag used by xfs.  btrfs
> implements
> a similar scheme, but hidden behind a few layers of cruft.
> 
> > The segment constructor walks buffer_head lists
> > directly and is not integrated with the generic address_space
> > writeback path.
> 
> This sounds like word-soup to me.

Are you suggesting to re-write the commit message?

Thanks,
Slava.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap
  2026-07-28 23:19   ` Viacheslav Dubeyko
@ 2026-07-29  6:28     ` Christoph Hellwig
  0 siblings, 0 replies; 11+ messages in thread
From: Christoph Hellwig @ 2026-07-29  6:28 UTC (permalink / raw)
  To: Viacheslav Dubeyko
  Cc: Christoph Hellwig, konishi.ryusuke, linux-nilfs, linux-fsdevel

On Tue, Jul 28, 2026 at 04:19:05PM -0700, Viacheslav Dubeyko wrote:
> Are you suggesting to re-write the commit message?

Yes.  Just mention that you only converts reads because the existing
code only supports reads.


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-07-29  6:29 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 22:27 [PATCH 0/2] nilfs2: convert O_DIRECT reads to iomap Viacheslav Dubeyko
2026-07-24 22:27 ` [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads Viacheslav Dubeyko
2026-07-25 22:33   ` Ryusuke Konishi
2026-07-28  0:46     ` Viacheslav Dubeyko
2026-07-28  3:49       ` Ryusuke Konishi
2026-07-24 22:27 ` [PATCH 2/2] nilfs2: switch O_DIRECT reads to iomap Viacheslav Dubeyko
2026-07-25 23:00   ` Ryusuke Konishi
2026-07-27  9:43 ` [PATCH 0/2] nilfs2: convert " Christian Brauner
2026-07-28  3:54 ` Christoph Hellwig
2026-07-28 23:19   ` Viacheslav Dubeyko
2026-07-29  6:28     ` Christoph Hellwig

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.