diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2017-12-14 18:42:59 -0500 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2017-12-21 11:47:28 -0500 |
commit | 363e59baa4f76d3f97c0133ff7014cba3d90a7c3 (patch) | |
tree | fee3a061d7ab180e6637399edd30377f5ad3efdd | |
parent | 91aae6be4139b9e3902656d819e6af66e051bd7a (diff) |
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: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | fs/xfs/xfs_inode.c | 28 |
1 files changed, 19 insertions, 9 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index b41952a4ddd8..6f95bdb408ce 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -1487,6 +1487,24 @@ xfs_link( | |||
1487 | return error; | 1487 | return error; |
1488 | } | 1488 | } |
1489 | 1489 | ||
1490 | /* Clear the reflink flag and the cowblocks tag if possible. */ | ||
1491 | static void | ||
1492 | xfs_itruncate_clear_reflink_flags( | ||
1493 | struct xfs_inode *ip) | ||
1494 | { | ||
1495 | struct xfs_ifork *dfork; | ||
1496 | struct xfs_ifork *cfork; | ||
1497 | |||
1498 | if (!xfs_is_reflink_inode(ip)) | ||
1499 | return; | ||
1500 | dfork = XFS_IFORK_PTR(ip, XFS_DATA_FORK); | ||
1501 | cfork = XFS_IFORK_PTR(ip, XFS_COW_FORK); | ||
1502 | if (dfork->if_bytes == 0 && cfork->if_bytes == 0) | ||
1503 | ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; | ||
1504 | if (cfork->if_bytes == 0) | ||
1505 | xfs_inode_clear_cowblocks_tag(ip); | ||
1506 | } | ||
1507 | |||
1490 | /* | 1508 | /* |
1491 | * Free up the underlying blocks past new_size. The new size must be smaller | 1509 | * Free up the underlying blocks past new_size. The new size must be smaller |
1492 | * than the current size. This routine can be used both for the attribute and | 1510 | * than the current size. This routine can be used both for the attribute and |
@@ -1583,15 +1601,7 @@ xfs_itruncate_extents( | |||
1583 | if (error) | 1601 | if (error) |
1584 | goto out; | 1602 | goto out; |
1585 | 1603 | ||
1586 | /* | 1604 | xfs_itruncate_clear_reflink_flags(ip); |
1587 | * Clear the reflink flag if there are no data fork blocks and | ||
1588 | * there are no extents staged in the cow fork. | ||
1589 | */ | ||
1590 | if (xfs_is_reflink_inode(ip) && ip->i_cnextents == 0) { | ||
1591 | if (ip->i_d.di_nblocks == 0) | ||
1592 | ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; | ||
1593 | xfs_inode_clear_cowblocks_tag(ip); | ||
1594 | } | ||
1595 | 1605 | ||
1596 | /* | 1606 | /* |
1597 | * Always re-log the inode so that our permanent transaction can keep | 1607 | * Always re-log the inode so that our permanent transaction can keep |