Re: [PATCH 1/2] nilfs2: add iomap operations for direct I/O reads

Viacheslav Dubeyko <[email protected]> Mon, 27 Jul 2026 17:46:00 -0700
Newsgroups org.kernel.vger.linux-nilfs,org.kernel.vger.linux-fsdevel
Message-ID <[email protected]>
Hi Ryusuke,

On Sun, 2026-07-26 at 07:33 +0900, Ryusuke Konishi wrote:
> Hi Viacheslav,
>=20
> On Sat, Jul 25, 2026 at 7:28=E2=80=AFAM Viacheslav Dubeyko wrote:
> >=20
> > 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.
> >=20
> > 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()).
> >=20
> > Signed-off-by: Viacheslav Dubeyko <[email protected]>
> > cc: Christoph Hellwig <[email protected]>
> > cc: Ryusuke Konishi <[email protected]>
> > cc: [email protected]
> > cc: [email protected]
> > ---
> > =C2=A0fs/nilfs2/Makefile |=C2=A0 2 +-
> > =C2=A0fs/nilfs2/iomap.c=C2=A0 | 71
> > ++++++++++++++++++++++++++++++++++++++++++++++
> > =C2=A0fs/nilfs2/iomap.h=C2=A0 | 13 +++++++++
> > =C2=A03 files changed, 85 insertions(+), 1 deletion(-)
> > =C2=A0create mode 100644 fs/nilfs2/iomap.c
> > =C2=A0create mode 100644 fs/nilfs2/iomap.h
> >=20
> > 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) +=3D nilfs2.o
> > =C2=A0nilfs2-y :=3D inode.o file.o dir.o super.o namei.o page.o mdt.o \
> > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 btnode.o bmap.o btree.o dire=
ct.o dat.o recovery.o \
> > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 the_nilfs.o segbuf.o segment=
.o cpfile.o sufile.o \
> > -=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ifile.o alloc.o gcinode.o ioctl.o=
 sysfs.o
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 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,
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 loff_t length, unsigned int flags,
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 struct iomap *iomap, struct iomap
> > *srcmap)
> > +{
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct the_nilfs *nilfs =3D inode=
->i_sb->s_fs_info;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 struct nilfs_inode_info *ii =3D N=
ILFS_I(inode);
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 sector_t blkoff =3D offset >> ino=
de->i_blkbits;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsigned int maxblocks;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 __u64 blknum =3D 0;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 int ret;
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /* Completely beyond EOF. Treat a=
s hole */
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (i_size_read(inode) <=3D offse=
t) {
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->type =3D IOMAP_HOLE;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->addr =3D IOMAP_NULL_ADDR;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->offset =3D offset;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->length =3D length;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 return 0;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /* Clamp length if the requested =
range goes beyond i_size
> > */
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (offset + length > i_size_read=
(inode)) {
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 loff_t i_size =3D i_size_read(inode);
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 unsigned int blocksize =3D i_blocksize(inode);
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 length =3D round_up(i_size, blocksize) - offset;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 maxblocks =3D min_t(loff_t, lengt=
h >> inode->i_blkbits,
> > INT_MAX);
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (maxblocks =3D=3D 0)
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 maxblocks =3D 1;
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 down_read(&NILFS_MDT(nilfs->ns_da=
t)->mi_sem);
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ret =3D nilfs_bmap_lookup_contig(=
ii->i_bmap, blkoff, &blknum,
> > maxblocks);
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 up_read(&NILFS_MDT(nilfs->ns_dat)=
->mi_sem);
> > +
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (ret =3D=3D -ENOENT) {
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->type =3D IOMAP_HOLE;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->addr =3D IOMAP_NULL_ADDR;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->offset =3D offset;
> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 iomap->length =3D min_t(loff_t, length,
> > i_blocksize(inode));
>=20
> 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.
>=20
> 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.