Re: [f2fs-dev] [PATCH v14 14/21] xfs: add flags to xfs_free_eofblocks() to pass down to block processing

Andrey Albershteyn via Linux-f2fs-devel <[email protected]> Mon, 10 Aug 2026 12:57:33 +0200
Newsgroups net.sourceforge.lists.linux-f2fs-devel,dev.linux.lists.fsverity,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-ext4,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-xfs
Message-ID <[email protected]>
On 2026-08-04 11:18:48, Darrick J. Wong wrote:
> On Mon, Aug 03, 2026 at 10:08:04PM +0200, Andrey Albershteyn wrote:
> > Add a flags parameter to xfs_free_eofblocks() to support selective
> > extent unmapping. Add two flags for unmapping all extents (unwritten and
> > normal) and fsverity leftover extents (only unwritten ones, leaving
> > normal in place).
> 
> Er, why do we need this?  Is this to clear out unwritten merkle tree
> blocks after a failed fsverity enrollment or something?  The commit
> message should say a little bit more about why anyone needs this.

Ok sure I will extend the message

> 
> > Signed-off-by: Andrey Albershteyn <[email protected]>
> > ---
> >  fs/xfs/libxfs/xfs_bmap.c | 56 +++++++++++++++++++++++++++++-----------
> >  fs/xfs/libxfs/xfs_bmap.h |  6 ++++-
> >  fs/xfs/xfs_bmap_util.c   | 20 ++++++++++----
> >  fs/xfs/xfs_bmap_util.h   | 13 +++++++++-
> >  fs/xfs/xfs_file.c        |  2 +-
> >  fs/xfs/xfs_icache.c      |  2 +-
> >  fs/xfs/xfs_inode.c       |  2 +-
> >  7 files changed, 76 insertions(+), 25 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > index cc48f6e20e80..1d8d157a9dfa 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.c
> > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > @@ -6144,15 +6144,12 @@ xfs_bmap_validate_extent(
> >  			XFS_IS_REALTIME_INODE(ip), whichfork, irec);
> >  }
> >  
> > -/*
> > - * Used in xfs_itruncate_extents().  This is the maximum number of extents
> > - * freed from a file in a single transaction.
> > - */
> > -#define	XFS_ITRUNC_MAX_EXTENTS	2
> > -
> >  /*
> >   * Unmap every extent in part of an inode's fork.  We don't do any higher level
> >   * invalidation work at all.
> > + *
> > + * The XFS_BMAPI_UNWRITTEN could be passed to remove only unwritten extents,
> > + * leaving out normal extents in place.
> 
> What flags do we support here?  ATTRFORK, NODISCARD, and UNWRITTEN?  Can
> that be documented in the comment or turned into asserts?

The xfs_bmapi_read has read_flags and ASSERT inside, the other flags
are then passed to xfs_bunmapi which probably be a better place for
an assert.

> 
> >   */
> >  int
> >  xfs_bunmapi_range(
> > @@ -6162,23 +6159,52 @@ xfs_bunmapi_range(
> >  	xfs_fileoff_t		startoff,
> >  	xfs_fileoff_t		endoff)
> >  {
> > -	xfs_filblks_t		unmap_len = endoff - startoff + 1;
> >  	int			error = 0;
> > +	int			nimaps = 1;
> > +	int			done = 0;
> > +	struct xfs_bmbt_irec	imap;
> > +	int			read_flags =
> > +			flags & (XFS_BMAPI_ATTRFORK | XFS_BMAPI_ENTIRE);
> 
> Why BMAPI_ENTIRE?
> 

These two flags is what xfs_bmapi_read() takes (assert inside).

> > +	xfs_exntst_t		exntst = XFS_EXT_NORM;
> >  
> >  	xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
> >  
> > -	while (unmap_len > 0) {
> > -		ASSERT((*tpp)->t_highest_agno == NULLAGNUMBER);
> > -		error = __xfs_bunmapi(*tpp, ip, startoff, &unmap_len, flags,
> > -				XFS_ITRUNC_MAX_EXTENTS);
> > +	if (flags & XFS_BMAPI_UNWRITTEN)
> > +		exntst = XFS_EXT_UNWRITTEN;
> > +
> > +	while (startoff < endoff) {
> > +		nimaps = 1;
> > +
> > +		error = xfs_bmapi_read(ip, startoff, endoff - startoff + 1,
> > +				&imap, &nimaps, read_flags);
> >  		if (error)
> >  			goto out;
> >  
> > -		/* free the just unmapped extents */
> > -		error = xfs_defer_finish(tpp);
> > -		if (error)
> > +		if (nimaps == 0)
> >  			goto out;
> > -		cond_resched();
> > +
> > +		if ((exntst == XFS_EXT_UNWRITTEN) &&
> > +				(imap.br_state != exntst)) {
> 
> No need for parentheses around these condition checks.
> 
> Would it be clearer if this was:
> 
> 		/* caller only wants to unmap unwritten extents */
> 		if ((flags & XFS_BMAPI_UNWRITTEN) &&
> 		    imap.br_state != XFS_EXT_UNWRITTEN) {
> 			...
> 		}

Thanks, sure will change it

> 
> > +			startoff = imap.br_startoff + imap.br_blockcount;
> > +			continue;
> > +		}
> > +
> > +		done = 0;
> > +		while (!done) {
> > +			ASSERT((*tpp)->t_highest_agno == NULLAGNUMBER);
> > +			error = xfs_bunmapi(*tpp, ip, imap.br_startoff,
> > +					imap.br_blockcount, flags, 0, &done);
> > +			if (error)
> > +				goto out;
> > +
> > +			/* free the just unmapped extent */
> > +			error = xfs_defer_finish(tpp);
> > +			if (error)
> > +				goto out;
> > +			cond_resched();
> > +		}
> > +
> > +		startoff = imap.br_startoff + imap.br_blockcount;
> >  	}
> >  out:
> >  	return error;
> > diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> > index d5f2729305fa..0f36431d9936 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.h
> > +++ b/fs/xfs/libxfs/xfs_bmap.h
> > @@ -90,6 +90,9 @@ struct xfs_bmalloca {
> >  /* Try to align allocations to the extent size hint */
> >  #define XFS_BMAPI_EXTSZALIGN	(1u << 11)
> >  
> > +/* Process unwritten extents only. Used for unmapping */
> > +#define XFS_BMAPI_UNWRITTEN	(1u << 12)
> > +
> >  #define XFS_BMAPI_FLAGS \
> >  	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
> >  	{ XFS_BMAPI_METADATA,	"METADATA" }, \
> > @@ -102,7 +105,8 @@ struct xfs_bmalloca {
> >  	{ XFS_BMAPI_COWFORK,	"COWFORK" }, \
> >  	{ XFS_BMAPI_NODISCARD,	"NODISCARD" }, \
> >  	{ XFS_BMAPI_NORMAP,	"NORMAP" },\
> > -	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }
> > +	{ XFS_BMAPI_EXTSZALIGN,	"EXTSZALIGN" }, \
> > +	{ XFS_BMAPI_UNWRITTEN,	"UNWRITTEN" }
> >  
> >  
> >  static inline int xfs_bmapi_aflag(int w)
> > diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> > index c88b9ade7389..6323eac48fc8 100644
> > --- a/fs/xfs/xfs_bmap_util.c
> > +++ b/fs/xfs/xfs_bmap_util.c
> > @@ -574,11 +574,13 @@ xfs_can_free_eofblocks(
> >   */
> >  int
> >  xfs_free_eofblocks(
> > -	struct xfs_inode	*ip)
> > +	struct xfs_inode	*ip,
> > +	int			flags)
> >  {
> >  	struct xfs_trans	*tp;
> >  	struct xfs_mount	*mp = ip->i_mount;
> >  	int			error;
> > +	int			bmapi_flags = XFS_BMAPI_NODISCARD;
> >  
> >  	/* Attach the dquots to the inode up front. */
> >  	error = xfs_qm_dqattach(ip);
> > @@ -593,15 +595,20 @@ xfs_free_eofblocks(
> >  	 *
> >  	 * Note that this means we also leave speculative preallocations in
> >  	 * place for preallocated files.
> > +	 *
> > +	 * Clean up delalloc reservations for fsverity too as those won't be
> > +	 * used
> >  	 */
> > -	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
> > +	if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND) ||
> > +			(flags & XFS_FREE_FSVERITY)) {
> 
> Oh, this patch keeps going after adding new flags to xfs_bunmapi.
> 
> Uh, this part should be a separate patch then.

Ok, sure, I will split it

> 
> >  		if (ip->i_delayed_blks) {
> >  			xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK,
> >  				round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
> >  				LLONG_MAX, NULL);
> >  		}
> >  		xfs_inode_clear_eofblocks_tag(ip);
> > -		return 0;
> > +		if (!(flags & XFS_FREE_FSVERITY))
> > +			return 0;
> >  	}
> >  
> >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
> > @@ -613,6 +620,9 @@ xfs_free_eofblocks(
> >  	xfs_ilock(ip, XFS_ILOCK_EXCL);
> >  	xfs_trans_ijoin(tp, ip, 0);
> >  
> > +	if (flags & XFS_FREE_FSVERITY)
> > +		bmapi_flags |= XFS_BMAPI_UNWRITTEN;
> > +
> >  	/*
> >  	 * Do not update the on-disk file size.  If we update the on-disk file
> >  	 * size and then the system crashes before the contents of the file are
> > @@ -620,7 +630,7 @@ xfs_free_eofblocks(
> >  	 * bug).
> >  	 */
> >  	error = xfs_itruncate_extents_flags(&tp, ip, XFS_DATA_FORK,
> > -				XFS_ISIZE(ip), XFS_BMAPI_NODISCARD);
> > +				XFS_ISIZE(ip), bmapi_flags);
> >  	if (error)
> >  		goto err_cancel;
> >  
> > @@ -928,7 +938,7 @@ xfs_prepare_shift(
> >  	 * into the accessible region of the file.
> >  	 */
> >  	if (xfs_can_free_eofblocks(ip)) {
> > -		error = xfs_free_eofblocks(ip);
> > +		error = xfs_free_eofblocks(ip, XFS_FREE_ALL);
> >  		if (error)
> >  			return error;
> >  	}
> > diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
> > index eaaf094154b9..9ea3000466cc 100644
> > --- a/fs/xfs/xfs_bmap_util.h
> > +++ b/fs/xfs/xfs_bmap_util.h
> > @@ -64,9 +64,20 @@ int	xfs_collapse_file_space(struct xfs_inode *, xfs_off_t offset,
> >  int	xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset,
> >  		xfs_off_t len);
> >  
> > +/*
> > + * Remove all extents and reservations beyond EOF
> > + */
> > +#define XFS_FREE_ALL		0
> > +
> > +/*
> > + * Do the normal post EOF cleaning except don't remove normal extents, in other
> > + * words, remove unwritten, delayed allocation and cow reservations
> 
> IOWs it preserves written blocks storing a merkle tree?

yes

> 
> > + */
> > +#define XFS_FREE_FSVERITY	1
> 
> /me wonders if this should be XFS_FREE_EOF_{ALL,PRESERVE_MERKLE} ?
> 
> Also, can we decide this from the XFS_DIFLAG2_VERITY state?

I suppose we can, then these XFS_FREE_* flags aren't necessary

-- 
- Andrey


_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel