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

Andrey Albershteyn <[email protected]>
Newsgroups org.kernel.vger.linux-ext4,dev.linux.lists.fsverity,net.sourceforge.lists.linux-f2fs-devel,org.kernel.vger.linux-btrfs,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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.