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.