Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 363e59ba authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: don't be so eager to clear the cowblocks tag on truncate



Currently, xfs_itruncate_extents clears the cowblocks tag if i_cnextents
is zero.  This is wrong, since i_cnextents only tracks real extents in
the CoW fork, which means that we could have some delayed CoW
reservations still in there that will now never get cleaned.

Fix a further bug where we /don't/ clear the reflink iflag if there are
any attribute blocks -- really, it's only safe to clear the reflink flag
if there are no data fork extents and no cow fork extents.

Found by adding clonerange to fsstress in xfs/017.

Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 91aae6be
Loading
Loading
Loading
Loading
+19 −9
Original line number Diff line number Diff line
@@ -1487,6 +1487,24 @@ xfs_link(
	return error;
}

/* Clear the reflink flag and the cowblocks tag if possible. */
static void
xfs_itruncate_clear_reflink_flags(
	struct xfs_inode	*ip)
{
	struct xfs_ifork	*dfork;
	struct xfs_ifork	*cfork;

	if (!xfs_is_reflink_inode(ip))
		return;
	dfork = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
	cfork = XFS_IFORK_PTR(ip, XFS_COW_FORK);
	if (dfork->if_bytes == 0 && cfork->if_bytes == 0)
		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
	if (cfork->if_bytes == 0)
		xfs_inode_clear_cowblocks_tag(ip);
}

/*
 * Free up the underlying blocks past new_size.  The new size must be smaller
 * than the current size.  This routine can be used both for the attribute and
@@ -1583,15 +1601,7 @@ xfs_itruncate_extents(
	if (error)
		goto out;

	/*
	 * Clear the reflink flag if there are no data fork blocks and
	 * there are no extents staged in the cow fork.
	 */
	if (xfs_is_reflink_inode(ip) && ip->i_cnextents == 0) {
		if (ip->i_d.di_nblocks == 0)
			ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
		xfs_inode_clear_cowblocks_tag(ip);
	}
	xfs_itruncate_clear_reflink_flags(ip);

	/*
	 * Always re-log the inode so that our permanent transaction can keep