[PATCH 03/14] libxfs: support unmapping reflink blocks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When we're unmapping blocks from a file, we need to decrease refcounts
in the btree and only free blocks if they refcount is 1.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/libxfs/xfs_bmap.c          |    5 +
 fs/xfs/libxfs/xfs_reflink_btree.c |  140 +++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_reflink_btree.h |    4 +
 3 files changed, 147 insertions(+), 2 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 057fa9a..3f5e8da 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_reflink_btree.h"
 
 
 kmem_zone_t		*xfs_bmap_free_item_zone;
@@ -4984,8 +4985,8 @@ 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);
+		xfs_reflink_bmap_add_free(mp, flist, del->br_startblock,
+					  del->br_blockcount, ip->i_ino, tp);
 	/*
 	 * Adjust inode # blocks in the file.
 	 */
diff --git a/fs/xfs/libxfs/xfs_reflink_btree.c b/fs/xfs/libxfs/xfs_reflink_btree.c
index 380ed72..f40ba1f 100644
--- a/fs/xfs/libxfs/xfs_reflink_btree.c
+++ b/fs/xfs/libxfs/xfs_reflink_btree.c
@@ -935,3 +935,143 @@ error0:
 	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
 	return error;
 }
+
+/**
+ * xfs_reflink_bmap_add_free() - release a range of blocks
+ *
+ * @mp: XFS mount object
+ * @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
+ * @tp: transaction that goes with the free operation
+ */
+int
+xfs_reflink_bmap_add_free(
+	struct xfs_mount	*mp,		/* mount point structure */
+	xfs_bmap_free_t		*flist,		/* list of extents */
+	xfs_fsblock_t		fsbno,		/* fs block number of extent */
+	xfs_filblks_t		fslen,		/* length of extent */
+	uint64_t		owner,		/* extent owner */
+	struct xfs_trans	*tp)		/* transaction */
+{
+	struct xfs_btree_cur	*cur;
+	int			error;
+	struct xfs_buf		*agbp;
+	xfs_agnumber_t		agno;		/* allocation group number */
+	xfs_agblock_t		agbno;		/* ag start of range to free */
+	xfs_agblock_t		agbend;		/* ag end of range to free */
+	xfs_extlen_t		aglen;		/* ag length of range to free */
+	int			i, have;
+	xfs_agblock_t		lbno;		/* rlextent start */
+	xfs_extlen_t		llen;		/* rlextent length */
+	xfs_nlink_t		lnr;		/* rlextent refcount */
+	xfs_agblock_t		bno;		/* rlext block # in loop */
+	xfs_extlen_t		len;		/* rlext length in loop */
+	unsigned long long	blocks_freed;
+	xfs_fsblock_t		range_fsb;
+
+	if (!xfs_sb_version_hasreflink(&mp->m_sb)) {
+		xfs_bmap_add_free(mp, flist, fsbno, fslen, owner);
+		return 0;
+	}
+
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	agbno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	CHECK_AG_NUMBER(mp, agno);
+	ASSERT(fslen < mp->m_sb.sb_agblocks);
+	CHECK_AG_EXTENT(mp, agbno, fslen);
+	aglen = fslen;
+
+	/*
+	 * Drop reference counts in the reflink tree.
+	 */
+	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
+	if (error)
+		return error;
+
+	/*
+	 * Grab a rl btree cursor.
+	 */
+	cur = xfs_reflinkbt_init_cursor(mp, tp, agbp, agno);
+	bno = agbno;
+	len = aglen;
+	agbend = agbno + aglen - 1;
+	blocks_freed = 0;
+
+	/*
+	 * Account for a left extent that partially covers our range.
+	 */
+	error = xfs_reflink_lookup_le(cur, bno, &have);
+	if (error)
+		goto error0;
+	if (have) {
+		error = xfs_reflink_get_rec(cur, &lbno, &llen, &lnr, &i);
+		if (error)
+			goto error0;
+		XFS_WANT_CORRUPTED_RLEXT_GOTO(mp, i, lbno, llen, lnr, error0);
+		if (lbno + llen > bno) {
+			blocks_freed += min(len, lbno + llen - bno);
+			bno += blocks_freed;
+			len -= blocks_freed;
+		}
+	}
+
+	while (len > 0) {
+		/*
+		 * Go find the next rlext.
+		 */
+		range_fsb = XFS_AGB_TO_FSB(mp, agno, bno);
+		error = xfs_btree_increment(cur, 0, &have);
+		if (error)
+			goto error0;
+		if (!have) {
+			/*
+			 * There's no right rlextent, so free bno to the end.
+			 */
+			lbno = bno + len;
+			llen = 0;
+		} else {
+			/*
+			 * Find the next rlextent.
+			 */
+			error = xfs_reflink_get_rec(cur, &lbno, &llen,
+					&lnr, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_RLEXT_GOTO(mp, i, lbno, llen, lnr,
+						      error0);
+			if (lbno >= bno + len) {
+				lbno = bno + len;
+				llen = 0;
+			}
+		}
+
+		/*
+		 * Free everything up to the start of the rlextent and
+		 * account for still-mapped blocks.
+		 */
+		if (lbno - bno > 0) {
+			xfs_bmap_add_free(mp, flist, range_fsb, lbno - bno,
+					  owner);
+			len -= lbno - bno;
+			bno += lbno - bno;
+		}
+		llen = min(llen, agbend + 1 - lbno);
+		blocks_freed += llen;
+		len -= llen;
+		bno += llen;
+	}
+
+	xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+
+	error = xfs_reflinkbt_adjust_refcount(mp, tp, agbp, agno, agbno, aglen,
+					      -1);
+	xfs_trans_brelse(tp, agbp);
+
+	return error;
+error0:
+	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+	xfs_trans_brelse(tp, agbp);
+	return error;
+}
diff --git a/fs/xfs/libxfs/xfs_reflink_btree.h b/fs/xfs/libxfs/xfs_reflink_btree.h
index d0785ff..4ea0ac4 100644
--- a/fs/xfs/libxfs/xfs_reflink_btree.h
+++ b/fs/xfs/libxfs/xfs_reflink_btree.h
@@ -71,4 +71,8 @@ extern int xfs_reflinkbt_adjust_refcount(struct xfs_mount *, struct xfs_trans *,
 		struct xfs_buf *, xfs_agnumber_t, xfs_agblock_t, xfs_extlen_t,
 		int);
 
+extern int xfs_reflink_bmap_add_free(struct xfs_mount *mp,
+		xfs_bmap_free_t *flist, xfs_fsblock_t fsbno, xfs_filblks_t len,
+		uint64_t owner, struct xfs_trans *tp);
+
 #endif	/* __XFS_REFLINK_BTREE_H__ */

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs



[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux