[PATCH 02/14] libxfs: adjust refcounts in reflink btree

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

 



Provide a function to adjust the reference counts for a range of
blocks in the reflink btree.

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


diff --git a/fs/xfs/libxfs/xfs_reflink_btree.c b/fs/xfs/libxfs/xfs_reflink_btree.c
index 8a0fa5d..380ed72 100644
--- a/fs/xfs/libxfs/xfs_reflink_btree.c
+++ b/fs/xfs/libxfs/xfs_reflink_btree.c
@@ -529,3 +529,409 @@ xfs_reflinkbt_delete(
 error0:
 	return error;
 }
+
+#ifdef REFLINK_DEBUG
+static void
+dump_cur_loc(
+	struct xfs_btree_cur	*cur,
+	const char		*str,
+	int			line)
+{
+	xfs_agblock_t		gbno;
+	xfs_extlen_t		glen;
+	xfs_nlink_t		gnr;
+	int			i;
+
+	xfs_reflink_get_rec(cur, &gbno, &glen, &gnr, &i);
+	printk(KERN_INFO "%s(%d) cur[%d]:[%u,%u,%u,%d] ", str, line,
+	       cur->bc_ptrs[0], gbno, glen, gnr, i);
+	if (i && cur->bc_ptrs[0]) {
+		cur->bc_ptrs[0]--;
+		xfs_reflink_get_rec(cur, &gbno, &glen, &gnr, &i);
+		printk("left[%d]:[%u,%u,%u,%d] ", cur->bc_ptrs[0],
+		       gbno, glen, gnr, i);
+		cur->bc_ptrs[0]++;
+	}
+
+	if (i && cur->bc_ptrs[0] < xfs_reflinkbt_get_maxrecs(cur, 0)) {
+		cur->bc_ptrs[0]++;
+		xfs_reflink_get_rec(cur, &gbno, &glen, &gnr, &i);
+		printk("right[%d]:[%u,%u,%u,%d] ", cur->bc_ptrs[0],
+		       gbno, glen, gnr, i);
+		cur->bc_ptrs[0]--;
+	}
+	printk("\n");
+}
+#else
+# define dump_cur_loc(c, s, l)
+#endif
+
+/*
+ * Adjust the ref count of a range of AG blocks.
+ */
+int						/* error */
+xfs_reflinkbt_adjust_refcount(
+	struct xfs_mount	*mp,
+	struct xfs_trans	*tp,		/* transaction pointer */
+	struct xfs_buf		*agbp,		/* buffer for agf structure */
+	xfs_agnumber_t		agno,		/* allocation group number */
+	xfs_agblock_t		agbno,		/* start of range */
+	xfs_extlen_t		aglen,		/* length of range */
+	int			adj)		/* how much to change refcnt */
+{
+	struct xfs_btree_cur	*cur;
+	int			error;
+	int			i, have;
+	bool			real_crl;	/* cbno/clen is on disk? */
+	xfs_agblock_t		lbno, cbno, rbno;	/* rlextent start */
+	xfs_extlen_t		llen, clen, rlen;	/* rlextent length */
+	xfs_nlink_t		lnr, cnr, rnr;		/* rlextent refcount */
+
+	xfs_agblock_t		bno;		/* ag bno in the loop */
+	xfs_agblock_t		agbend;		/* end agbno of the loop */
+	xfs_extlen_t		len;		/* remaining len to add */
+	xfs_nlink_t		new_cnr;	/* new refcount */
+
+	CHECK_AG_NUMBER(mp, agno);
+	CHECK_AG_EXTENT(mp, agbno, aglen);
+	ASSERT(adj == -1 || adj == 1);
+
+	/*
+	 * Allocate/initialize a cursor for the by-number freespace btree.
+	 */
+	cur = xfs_reflinkbt_init_cursor(mp, tp, agbp, agno);
+
+	/*
+	 * Split a left rlextent that crosses agbno.
+	 */
+	error = xfs_reflink_lookup_le(cur, agbno, &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 < agbno && lbno + llen > agbno) {
+			dbg_printk("split lext crossing agbno [%u:%u:%u]\n",
+				   lbno, llen, lnr);
+			error = xfs_reflinkbt_update(cur, lbno, agbno - lbno,
+					lnr);
+			if (error)
+				goto error0;
+
+			error = xfs_btree_increment(cur, 0, &i);
+			if (error)
+				goto error0;
+
+			error = xfs_reflinkbt_insert(cur, agbno,
+					llen - (agbno - lbno), lnr, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+		}
+	}
+
+	/*
+	 * Split a right rlextent that crosses agbno.
+	 */
+	agbend = agbno + aglen - 1;
+	error = xfs_reflink_lookup_le(cur, agbend, &have);
+	if (error)
+		goto error0;
+	if (have) {
+		error = xfs_reflink_get_rec(cur, &rbno, &rlen, &rnr, &i);
+		if (error)
+			goto error0;
+		XFS_WANT_CORRUPTED_RLEXT_GOTO(mp, i, rbno, rlen, rnr, error0);
+		if (agbend + 1 != mp->m_sb.sb_agblocks &&
+		    agbend + 1 < rbno + rlen) {
+			dbg_printk("split rext crossing agbend [%u:%u:%u]\n",
+				   rbno, rlen, rnr);
+			error = xfs_reflinkbt_update(cur, agbend + 1,
+					rlen - (agbend - rbno + 1), rnr);
+			if (error)
+				goto error0;
+
+			error = xfs_reflinkbt_insert(cur, rbno,
+					agbend - rbno + 1, rnr, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+		}
+	}
+
+	/*
+	 * Start iterating the range we're adjusting.  rlextent boundaries
+	 * should be at agbno and agbend.
+	 */
+	bno = agbno;
+	len = aglen;
+	while (len > 0) {
+		llen = clen = rlen = 0;
+		real_crl = false;
+		/*
+		 * Look up the current and left rlextents.
+		 */
+		error = xfs_reflink_lookup_le(cur, bno, &have);
+		if (error)
+			goto error0;
+		if (have) {
+			error = xfs_reflink_get_rec(cur, &cbno, &clen, &cnr,
+						    &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_RLEXT_GOTO(mp, i, cbno, clen, cnr,
+						      error0);
+			if (cbno != bno) {
+				/*
+				 * bno points to a hole; this is the left rlext.
+				 */
+				ASSERT((unsigned long long)lbno + llen <= bno);
+				lbno = cbno;
+				llen = clen;
+				lnr = cnr;
+
+				cbno = bno;
+				clen = len;
+				cnr = 1;
+			} else {
+				real_crl = true;
+				/*
+				 * Go find the left rlext.
+				 */
+				error = xfs_btree_decrement(cur, 0, &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);
+					ASSERT((unsigned long long)lbno + llen <= bno);
+				}
+				error = xfs_btree_increment(cur, 0, &have);
+				if (error)
+					goto error0;
+			}
+		} else {
+			/*
+			 * No left extent; just invent our current rlextent.
+			 */
+			cbno = bno;
+			clen = len;
+			cnr = 1;
+		}
+
+		/*
+		 * If the left rlext isn't adjacent, forget about it.
+		 */
+		if (llen > 0 && lbno + llen != bno)
+			llen = 0;
+
+		/*
+		 * Look up the right rlextent.
+		 */
+		error = xfs_btree_increment(cur, 0, &have);
+		if (error)
+			goto error0;
+		if (have) {
+			error = xfs_reflink_get_rec(cur, &rbno, &rlen, &rnr,
+						    &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_RLEXT_GOTO(mp, i, rbno, rlen, rnr,
+						      error0);
+			if (agbno + aglen < rbno)
+				rlen = 0;
+			if (!real_crl)
+				clen = min(clen, rbno - cbno);
+			ASSERT((unsigned long long)cbno + clen <= rbno);
+		}
+
+		/*
+		 * Point the cursor to cbno (or where it will be inserted).
+		 */
+		if (real_crl) {
+			error = xfs_btree_decrement(cur, 0, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+		}
+		ASSERT(clen > 0);
+		ASSERT(cbno == bno);
+		ASSERT(cbno >= agbno);
+		ASSERT((unsigned long long)cbno + clen <=
+		       (unsigned long long)agbno + aglen);
+		if (real_crl)
+			ASSERT(cnr > 1);
+		else
+			ASSERT(cnr == 1);
+		new_cnr = cnr + adj;
+
+#ifdef REFLINK_DEBUG
+		{
+		xfs_agblock_t gbno;
+		xfs_extlen_t glen;
+		xfs_nlink_t gnr;
+		xfs_reflink_get_rec(cur, &gbno, &glen, &gnr, &i);
+		printk(KERN_ERR "%s: insert ag=%u [%u:%u:%d] ", __func__,
+		       agno, agbno, agbend, adj);
+		if (llen)
+			printk("l:[%u,%u,%u] ", lbno, llen, lnr);
+		printk("[%u,%u,%u,%d] ", cbno, clen, cnr, real_crl);
+		if (rlen)
+			printk("r:[%u,%u,%u] ", rbno, rlen, rnr);
+		printk("\n");
+		dump_cur_loc(cur, "cur", __LINE__);
+		}
+#endif
+		/*
+		 * Nothing to do when unmapping a range of blocks with
+		 * a single owner.
+		 */
+		if (new_cnr == 0) {
+			dbg_printk("single-owner blocks; ignoring");
+			goto advloop;
+		}
+
+		/*
+		 * These blocks have hit MAXRLCOUNT; keep it that way.
+		 */
+		if (cnr == MAXRLCOUNT) {
+			dbg_printk("hit MAXRLCOUNT; moving on");
+			goto advloop;
+		}
+
+		/*
+		 * Try to merge with left and right rlexts outside range.
+		 */
+		if (llen > 0 && rlen > 0 &&
+		    lbno + llen == agbno &&
+		    rbno == agbend + 1 &&
+		    lbno + llen + clen == rbno &&
+		    (unsigned long long)llen + clen + rlen < MAXRLEXTLEN &&
+		    lnr == rnr &&
+		    lnr == new_cnr) {
+			dbg_printk("merge l/c/rext\n");
+			error = xfs_reflinkbt_delete(cur, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			if (real_crl) {
+				error = xfs_reflinkbt_delete(cur, &i);
+				if (error)
+					goto error0;
+				XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			}
+
+			error = xfs_btree_decrement(cur, 0, &have);
+			if (error)
+				goto error0;
+			error = xfs_reflinkbt_update(cur, lbno,
+					llen + clen + rlen, lnr);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, have == 1, error0);
+			break;
+		}
+
+		/*
+		 * Try to merge with left rlext outside the range.
+		 */
+		if (llen > 0 &&
+		    lbno + llen == agbno &&
+		    lnr == new_cnr &&
+		    (unsigned long long)llen + clen < MAXRLEXTLEN) {
+			dbg_printk("merge l/cext\n");
+			if (real_crl) {
+				error = xfs_reflinkbt_delete(cur, &i);
+				if (error)
+					goto error0;
+				XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			}
+
+			error = xfs_btree_decrement(cur, 0, &have);
+			if (error)
+				goto error0;
+			error = xfs_reflinkbt_update(cur, lbno,
+					llen + clen, lnr);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, have == 1, error0);
+			goto advloop;
+		}
+
+		/*
+		 * Try to merge with right rlext outside the range.
+		 */
+		if (rlen > 0 &&
+		    rbno == agbend + 1 &&
+		    rnr == new_cnr &&
+		    cbno + clen == rbno &&
+		    (unsigned long long)clen + rlen < MAXRLEXTLEN) {
+			dbg_printk("merge c/rext\n");
+			if (real_crl) {
+				error = xfs_reflinkbt_delete(cur, &i);
+				if (error)
+					goto error0;
+				XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			}
+
+			error = xfs_reflinkbt_update(cur, cbno,
+					clen + rlen, rnr);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, have == 1, error0);
+			break;
+		}
+
+		/*
+		 * rlext is no longer reflinked; remove it from tree.
+		 */
+		if (new_cnr == 1 && adj < 0) {
+			dbg_printk("remove cext\n");
+			ASSERT(real_crl == true);
+			error = xfs_reflinkbt_delete(cur, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			goto advloop;
+		}
+
+		/*
+		 * rlext needs to be added to the tree.
+		 */
+		if (new_cnr == 2 && adj > 0) {
+			dbg_printk("insert cext\n");
+			error = xfs_reflinkbt_insert(cur, cbno, clen,
+					new_cnr, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error0);
+			goto advloop;
+		}
+
+		/*
+		 * Update rlext.
+		 */
+		dbg_printk("update cext\n");
+		ASSERT(new_cnr >= 2);
+		error = xfs_reflinkbt_update(cur, cbno, clen, new_cnr);
+		if (error)
+			goto error0;
+
+advloop:
+		bno += clen;
+		len -= clen;
+	}
+
+	xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+	return 0;
+error0:
+	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+	return error;
+}
diff --git a/fs/xfs/libxfs/xfs_reflink_btree.h b/fs/xfs/libxfs/xfs_reflink_btree.h
index a27588a..d0785ff 100644
--- a/fs/xfs/libxfs/xfs_reflink_btree.h
+++ b/fs/xfs/libxfs/xfs_reflink_btree.h
@@ -67,4 +67,8 @@ extern int xfs_reflink_lookup_ge(struct xfs_btree_cur *cur, xfs_agblock_t bno,
 extern int xfs_reflink_get_rec(struct xfs_btree_cur *cur, xfs_agblock_t *bno,
 		xfs_extlen_t *len, xfs_nlink_t *nlink, int *stat);
 
+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);
+
 #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