[PATCH 09/24] libxfs: adjust refcount when unmapping file blocks

Darrick J. Wong
2015-07-29T22:34:12Z

When we're unmapping blocks from a reflinked file, decrease the
refcount of the affected blocks and free the extents that are no
longer in use.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_bmap.c | 16 +++++++++++++---
fs/xfs/libxfs/xfs_refcount.c | 42 ++++++++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_refcount.h | 4 ++++
3 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 057fa9a..dfdd9e6 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -45,6 +45,7 @@
#include "xfs_symlink.h"
#include "xfs_attr_leaf.h"
#include "xfs_filestream.h"
+#include "xfs_refcount.h"

kmem_zone_t *xfs_bmap_free_item_zone;
@@ -4983,9 +4984,18 @@ xfs_bmap_del_extent(
/*
* If we need to, add to list of extents to delete.
*/
- if (do_fx)
- xfs_bmap_add_free(mp, flist, del->br_startblock,
- del->br_blockcount, ip->i_ino);
+ if (do_fx) {
+ if (xfs_is_reflink_inode(ip)) {
+ error = xfs_refcount_put_extent(mp, tp, flist,
+ del->br_startblock,
+ del->br_blockcount, ip->i_ino);
+ if (error)
+ goto done;
+ } else
+ xfs_bmap_add_free(mp, flist, del->br_startblock,
+ del->br_blockcount, ip->i_ino);
+ }
+
/*
* Adjust inode # blocks in the file.
*/
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 83d5677..ef69375 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -939,3 +939,45 @@ xfs_refcount_decrease(
return xfs_refcountbt_adjust_refcount(mp, tp, agbp, agno, agbno,
aglen, -1, flist, owner);
}
+
+/**
+ * xfs_refcount_put_extent() - release a range of blocks
+ *
+ * @mp: XFS mount object
+ * @tp: transaction that goes with the free operation
+ * @flist: List of blocks to be freed at the end of the transaction
+ * @fsbno: First fs block of the range to release
+ * @len: Length of range
+ * @owner: owner of the extent
+ */
+int
+xfs_refcount_put_extent(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_bmap_free *flist,
+ xfs_fsblock_t fsbno,
+ xfs_filblks_t fslen,
+ uint64_t owner)
+{
+ int error;
+ struct xfs_buf *agbp;
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_agblock_t agbno; /* ag start of range to free */
+ xfs_extlen_t aglen; /* ag length of range to free */
+
+ agno = XFS_FSB_TO_AGNO(mp, fsbno);
+ agbno = XFS_FSB_TO_AGBNO(mp, fsbno);
+ aglen = fslen;
+
+ /*
+ * Drop reference counts in the refcount tree.
+ */
+ error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
+ if (error)
+ return error;
+
+ error = xfs_refcount_decrease(mp, tp, agbp, agno, agbno, aglen, flist,
+ owner);
+ xfs_trans_brelse(tp, agbp);
+ return error;
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index 11d773bb..649d679 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -34,4 +34,8 @@ extern int xfs_refcount_decrease(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_extlen_t aglen, struct xfs_bmap_free *flist,
uint64_t owner);

+extern int xfs_refcount_put_extent(struct xfs_mount *mp, struct xfs_trans *tp,
+ struct xfs_bmap_free *flist, xfs_fsblock_t fsbno,
+ xfs_filblks_t len, uint64_t owner);
+
#endif /* __XFS_REFCOUNT_H__ */