Skip to content

Commit

Permalink
xfs: don't be so eager to clear the cowblocks tag on truncate
Browse files Browse the repository at this point in the history
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: Darrick J. Wong <[email protected]>
Reviewed-by: Dave Chinner <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
  • Loading branch information
djwong committed Dec 21, 2017
1 parent 91aae6b commit 363e59b
Showing 1 changed file with 19 additions and 9 deletions.
28 changes: 19 additions & 9 deletions fs/xfs/xfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 363e59b

Please sign in to comment.