[PATCH 086/119] xfs: cancel CoW reservations and clear inode reflink flag when freeing blocks

Darrick J. Wong
2016-06-17T01:27:10Z

When we're freeing blocks (truncate, punch, etc.), clear all CoW
reservations in the range being freed. If the file block count
drops to zero, also clear the inode reflink flag.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_bmap_util.c | 34 ++++++++++++++++++++++++++++++++++
fs/xfs/xfs_inode.c | 13 +++++++++++++
2 files changed, 47 insertions(+)

diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 79225fb..9285111 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1147,6 +1147,32 @@ xfs_zero_remaining_bytes(
return error;
}

+STATIC int
+xfs_free_cow_space(
+ struct xfs_inode *ip,
+ struct xfs_trans **tpp,
+ xfs_fileoff_t startoffset_fsb,
+ xfs_fileoff_t endoffset_fsb)
+{
+ int error;
+
+ /* Remove any pending CoW reservations. */
+ error = xfs_reflink_cancel_cow_blocks(ip, tpp, startoffset_fsb,
+ endoffset_fsb);
+ if (error)
+ goto out;
+
+ /*
+ * Clear the reflink flag if we freed everything.
+ */
+ if (ip->i_d.di_nblocks == 0) {
+ ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+ xfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE);
+ }
+out:
+ return error;
+}
+
int
xfs_free_file_space(
struct xfs_inode *ip,
@@ -1294,6 +1320,14 @@ xfs_free_file_space(
if (error)
goto error0;

+ /* Remove CoW reservations and inode flag if applicable. */
+ if (done && xfs_is_reflink_inode(ip)) {
+ error = xfs_free_cow_space(ip, &tp, startoffset_fsb,
+ endoffset_fsb);
+ if (error)
+ goto error0;
+ }
+
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index e08eaea..b8d3c4f 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -49,6 +49,7 @@
#include "xfs_trans_priv.h"
#include "xfs_log.h"
#include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"

kmem_zone_t *xfs_inode_zone;

@@ -1586,6 +1587,18 @@ xfs_itruncate_extents(
goto out;
}

+ /* Remove all pending CoW reservations. */
+ error = xfs_reflink_cancel_cow_blocks(ip, &tp, first_unmap_block,
+ last_block);
+ if (error)
+ goto out;
+
+ /*
+ * Clear the reflink flag if we truncated everything.
+ */
+ if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip))
+ ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+
/*
* Always re-log the inode so that our permanent transaction can keep
* on rolling it forward in the log.