Re: [PATCH 3/6] xfs: flush eof folio before insert range size update

Brian Foster
2025-11-18T20:08:42Z

On Wed, Nov 05, 2025 at 10:34:26AM -0500, Brian Foster wrote:
> On Tue, Nov 04, 2025 at 04:14:45PM -0800, Darrick J. Wong wrote:
> > On Thu, Oct 16, 2025 at 03:03:00PM -0400, Brian Foster wrote:
> > > The flush in xfs_buffered_write_iomap_begin() for zero range over a
> > > data fork hole fronted by COW fork prealloc is primarily designed to
> > > provide correct zeroing behavior in particular pagecache conditions.
> > > As it turns out, this also partially masks some odd behavior in
> > > insert range (via zero range via setattr).
> > >
> > > Insert range bumps i_size the length of the new range, flushes,
> > > unmaps pagecache and cancels COW prealloc, and then right shifts
> > > extents from the end of the file back to the target offset of the
> > > insert. Since the i_size update occurs before the pagecache flush,
> > > this creates a transient situation where writeback around EOF can
> > > behave differently.
> >
> > Why not flush the file from @offset to EOF, flush the COW
> > preallocations, extend i_size, and only then start shifting extents?
> > That would seem a lot more straightforward to me.
> >
>
> I agree. I noted in the cover letter that I started with this approach
> of reordering the existing sequence of operations, but the factoring
> looked ugly enough that I stopped and wanted to solicit input.
>

Well this is annoying.. I looked into lifting the prepare shift bits a
level up and just invoking it before the truncate, but that actually
appears to be incorrect in at least one case. I.e., the truncate up for
insert range invokes zero eof for partial zeroing of the eof block,
which brings a folio back into pagecache (after the lifted flush/unmap)
within the range being shfited. This then creates a post-shift cache
inconsistency. This is resolved by restoring the flush/unmap between the
truncate and extent shift, so we end up just having it in both places.

So atm I'm not really sure if there's a better option than what this
patch is doing. There are probably other ways to implement it, like
perhaps introducing ability to convert blocks that are shifted out from
eof, but that just seems like unnecessary complexity for basically
implementing the same sort of thing.

I dunno.. insert range is odd in this regard. I'll think a bit more
about it while I look into the zero range cow mapping reporting thing.

Brian

> The details of that fell out of my brain since I posted this,
> unfortunately. I suspect it may have been related to layering or
> something wrt the prepare_shift factoring, but I'll take another look in
> that direction for v2 and once I've got some feedback on the rest of the
> series.. Thanks.
>
> Brian
>
> > --D
> >
> > > This appears to be corner case situation, but if happens to be
> > > fronted by COW fork speculative preallocation and a large, dirty
> > > folio that contains at least one full COW block beyond EOF, the
> > > writeback after i_size is bumped may remap that COW fork block into
> > > the data fork within EOF. The block is zeroed and then shifted back
> > > out to post-eof, but this is unexpected in that it leads to a
> > > written post-eof data fork block. This can cause a zero range
> > > warning on a subsequent size extension, because we should never find
> > > blocks that require physical zeroing beyond i_size.
> > >
> > > To avoid this quirk, flush the EOF folio before the i_size update
> > > during insert range. The entire range will be flushed, unmapped and
> > > invalidated anyways, so this should be relatively unnoticeable.
> > >
> > > Signed-off-by: Brian Foster <bfoster@redhat.com>
> > > ---
> > > fs/xfs/xfs_file.c | 17 +++++++++++++++++
> > > 1 file changed, 17 insertions(+)
> > >
> > > diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> > > index 5b9864c8582e..cc3a9674ad40 100644
> > > --- a/fs/xfs/xfs_file.c
> > > +++ b/fs/xfs/xfs_file.c
> > > @@ -1226,6 +1226,23 @@ xfs_falloc_insert_range(
> > > if (offset >= isize)
> > > return -EINVAL;
> > >
> > > + /*
> > > + * Let writeback clean up EOF folio state before we bump i_size. The
> > > + * insert flushes before it starts shifting and under certain
> > > + * circumstances we can write back blocks that should technically be
> > > + * considered post-eof (and thus should not be submitted for writeback).
> > > + *
> > > + * For example, a large, dirty folio that spans EOF and is backed by
> > > + * post-eof COW fork preallocation can cause block remap into the data
> > > + * fork. This shifts back out beyond EOF, but creates an expectedly
> > > + * written post-eof block. The insert is going to flush, unmap and
> > > + * cancel prealloc across this whole range, so flush EOF now before we
> > > + * bump i_size to provide consistent behavior.
> > > + */
> > > + error = filemap_write_and_wait_range(inode->i_mapping, isize, isize);
> > > + if (error)
> > > + return error;
> > > +
> > > error = xfs_falloc_setsize(file, isize + len);
> > > if (error)
> > > return error;
> > > --
> > > 2.51.0
> > >
> > >
> >
>
>