diff mbox series

[2/2] xfs: refactor xfs_iread_extents to use xfs_btree_visit_blocks

Message ID 157198052776.2873576.12691586684307027826.stgit@magnolia (mailing list archive)
State Superseded
Headers show
Series xfs: refactor open-coded bmbt walks | expand

Commit Message

Darrick J. Wong Oct. 25, 2019, 5:15 a.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

xfs_iread_extents open-codes everything in xfs_btree_visit_blocks, so
refactor the btree helper to be able to iterate only the records on
level 0, then port iread_extents to use it.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_bmap.c  |  191 ++++++++++++++++++---------------------------
 fs/xfs/libxfs/xfs_btree.c |    8 +-
 fs/xfs/libxfs/xfs_btree.h |    4 +
 fs/xfs/scrub/bitmap.c     |    3 -
 4 files changed, 88 insertions(+), 118 deletions(-)

Comments

Christoph Hellwig Oct. 25, 2019, 12:53 p.m. UTC | #1
On Thu, Oct 24, 2019 at 10:15:27PM -0700, Darrick J. Wong wrote:
> +struct xfs_iread_state {
> +	struct xfs_iext_cursor	icur;
> +	struct xfs_ifork	*ifp;

Given that the btree cursor contains the whichfork information there is
not real need to also pass a ifork pointer.

> +	xfs_extnum_t		loaded;
> +	xfs_extnum_t		nextents;

That can just use XFS_IFORK_NEXTENTS() directly in the callback.

> +	int			state;

Same here.  The xfs_bmap_fork_to_state is cheap enough to just do it
inside the callback function.

> +	block = xfs_btree_get_block(cur, level, &bp);

This is opencoded in almost all xfs_btree_visit_blocks callbacks.
Any chance we could simply pass the buffer to the callback?

> +/* Only visit record blocks. */
> +#define XFS_BTREE_VISIT_RECORDS_ONLY	(0x1)

I find these only flags a little weird.  I'd rather have two flags,
one to to visit interior nodes, and one to visit leaf nodes, which makes
the interface very much self-describing.
Darrick J. Wong Oct. 25, 2019, 5:25 p.m. UTC | #2
On Fri, Oct 25, 2019 at 05:53:32AM -0700, Christoph Hellwig wrote:
> On Thu, Oct 24, 2019 at 10:15:27PM -0700, Darrick J. Wong wrote:
> > +struct xfs_iread_state {
> > +	struct xfs_iext_cursor	icur;
> > +	struct xfs_ifork	*ifp;
> 
> Given that the btree cursor contains the whichfork information there is
> not real need to also pass a ifork pointer.
> 
> > +	xfs_extnum_t		loaded;
> > +	xfs_extnum_t		nextents;
> 
> That can just use XFS_IFORK_NEXTENTS() directly in the callback.
> 
> > +	int			state;
> 
> Same here.  The xfs_bmap_fork_to_state is cheap enough to just do it
> inside the callback function.

Ok.

> > +	block = xfs_btree_get_block(cur, level, &bp);
> 
> This is opencoded in almost all xfs_btree_visit_blocks callbacks.
> Any chance we could simply pass the buffer to the callback?

Ok.

> > +/* Only visit record blocks. */
> > +#define XFS_BTREE_VISIT_RECORDS_ONLY	(0x1)
> 
> I find these only flags a little weird.  I'd rather have two flags,
> one to to visit interior nodes, and one to visit leaf nodes, which makes
> the interface very much self-describing.

Hm, good suggestion, will do.

--D
diff mbox series

Patch

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 587889585a23..31a7c7768ea0 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1154,6 +1154,65 @@  xfs_bmap_add_attrfork(
  * Internal and external extent tree search functions.
  */
 
+struct xfs_iread_state {
+	struct xfs_iext_cursor	icur;
+	struct xfs_ifork	*ifp;
+	xfs_extnum_t		loaded;
+	xfs_extnum_t		nextents;
+	int			state;
+};
+
+/* Stuff every bmbt record from this block into the incore extent map. */
+static int
+xfs_iread_bmbt_block(
+	struct xfs_btree_cur	*cur,
+	int			level,
+	void			*priv)
+{
+	struct xfs_iread_state	*ir = priv;
+	struct xfs_mount	*mp = cur->bc_mp;
+	struct xfs_inode	*ip = cur->bc_private.b.ip;
+	struct xfs_btree_block	*block;
+	struct xfs_buf		*bp;
+	struct xfs_bmbt_rec	*frp;
+	xfs_extnum_t		num_recs;
+	xfs_extnum_t		j;
+	int			whichfork = cur->bc_private.b.whichfork;
+
+	block = xfs_btree_get_block(cur, level, &bp);
+
+	/* Abort if we find more records than nextents. */
+	num_recs = xfs_btree_get_numrecs(block);
+	if (unlikely(ir->loaded + num_recs > ir->nextents)) {
+		xfs_warn(ip->i_mount, "corrupt dinode %llu, (btree extents).",
+				(unsigned long long)ip->i_ino);
+		xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, block,
+				sizeof(*block), __this_address);
+		return -EFSCORRUPTED;
+	}
+
+	/* Copy records into the incore cache. */
+	frp = XFS_BMBT_REC_ADDR(mp, block, 1);
+	for (j = 0; j < num_recs; j++, frp++, ir->loaded++) {
+		struct xfs_bmbt_irec	new;
+		xfs_failaddr_t		fa;
+
+		xfs_bmbt_disk_get_all(frp, &new);
+		fa = xfs_bmap_validate_extent(ip, whichfork, &new);
+		if (fa) {
+			xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+					"xfs_iread_extents(2)", frp,
+					sizeof(*frp), fa);
+			return -EFSCORRUPTED;
+		}
+		xfs_iext_insert(ip, &ir->icur, &new, ir->state);
+		trace_xfs_read_extent(ip, &ir->icur, ir->state, _THIS_IP_);
+		xfs_iext_next(ir->ifp, &ir->icur);
+	}
+
+	return 0;
+}
+
 /*
  * Read in extents from a btree-format inode.
  */
@@ -1163,136 +1222,40 @@  xfs_iread_extents(
 	struct xfs_inode	*ip,
 	int			whichfork)
 {
+	struct xfs_iread_state	ir = {
+		.state		= xfs_bmap_fork_to_state(whichfork),
+		.ifp		= XFS_IFORK_PTR(ip, whichfork),
+		.nextents	= XFS_IFORK_NEXTENTS(ip, whichfork),
+	};
 	struct xfs_mount	*mp = ip->i_mount;
-	int			state = xfs_bmap_fork_to_state(whichfork);
-	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
-	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
-	struct xfs_btree_block	*block = ifp->if_broot;
-	struct xfs_iext_cursor	icur;
-	struct xfs_bmbt_irec	new;
-	xfs_fsblock_t		bno;
-	struct xfs_buf		*bp;
-	xfs_extnum_t		i, j;
-	int			level;
-	__be64			*pp;
+	struct xfs_btree_cur	*cur;
 	int			error;
 
 	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
 	if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
 		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
-		return -EFSCORRUPTED;
-	}
-
-	/*
-	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
-	 */
-	level = be16_to_cpu(block->bb_level);
-	if (unlikely(level == 0)) {
-		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
-		return -EFSCORRUPTED;
-	}
-	pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
-	bno = be64_to_cpu(*pp);
-
-	/*
-	 * Go down the tree until leaf level is reached, following the first
-	 * pointer (leftmost) at each level.
-	 */
-	while (level-- > 0) {
-		error = xfs_btree_read_bufl(mp, tp, bno, &bp,
-				XFS_BMAP_BTREE_REF, &xfs_bmbt_buf_ops);
-		if (error)
-			goto out;
-		block = XFS_BUF_TO_BLOCK(bp);
-		if (level == 0)
-			break;
-		pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
-		bno = be64_to_cpu(*pp);
-		XFS_WANT_CORRUPTED_GOTO(mp,
-			xfs_verify_fsbno(mp, bno), out_brelse);
-		xfs_trans_brelse(tp, bp);
+		error = -EFSCORRUPTED;
+		goto out;
 	}
 
-	/*
-	 * Here with bp and block set to the leftmost leaf node in the tree.
-	 */
-	i = 0;
-	xfs_iext_first(ifp, &icur);
-
-	/*
-	 * Loop over all leaf nodes.  Copy information to the extent records.
-	 */
-	for (;;) {
-		xfs_bmbt_rec_t	*frp;
-		xfs_fsblock_t	nextbno;
-		xfs_extnum_t	num_recs;
-
-		num_recs = xfs_btree_get_numrecs(block);
-		if (unlikely(i + num_recs > nextents)) {
-			xfs_warn(ip->i_mount,
-				"corrupt dinode %Lu, (btree extents).",
-				(unsigned long long) ip->i_ino);
-			xfs_inode_verifier_error(ip, -EFSCORRUPTED,
-					__func__, block, sizeof(*block),
-					__this_address);
-			error = -EFSCORRUPTED;
-			goto out_brelse;
-		}
-		/*
-		 * Read-ahead the next leaf block, if any.
-		 */
-		nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
-		if (nextbno != NULLFSBLOCK)
-			xfs_btree_reada_bufl(mp, nextbno, 1,
-					     &xfs_bmbt_buf_ops);
-		/*
-		 * Copy records into the extent records.
-		 */
-		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
-		for (j = 0; j < num_recs; j++, frp++, i++) {
-			xfs_failaddr_t	fa;
-
-			xfs_bmbt_disk_get_all(frp, &new);
-			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
-			if (fa) {
-				error = -EFSCORRUPTED;
-				xfs_inode_verifier_error(ip, error,
-						"xfs_iread_extents(2)",
-						frp, sizeof(*frp), fa);
-				goto out_brelse;
-			}
-			xfs_iext_insert(ip, &icur, &new, state);
-			trace_xfs_read_extent(ip, &icur, state, _THIS_IP_);
-			xfs_iext_next(ifp, &icur);
-		}
-		xfs_trans_brelse(tp, bp);
-		bno = nextbno;
-		/*
-		 * If we've reached the end, stop.
-		 */
-		if (bno == NULLFSBLOCK)
-			break;
-		error = xfs_btree_read_bufl(mp, tp, bno, &bp,
-				XFS_BMAP_BTREE_REF, &xfs_bmbt_buf_ops);
-		if (error)
-			goto out;
-		block = XFS_BUF_TO_BLOCK(bp);
-	}
+	cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
+	error = xfs_btree_visit_blocks(cur, xfs_iread_bmbt_block,
+			XFS_BTREE_VISIT_RECORDS_ONLY, &ir);
+	xfs_btree_del_cursor(cur, error);
+	if (error)
+		goto out;
 
-	if (i != XFS_IFORK_NEXTENTS(ip, whichfork)) {
+	if (ir.loaded != ir.nextents) {
 		error = -EFSCORRUPTED;
 		goto out;
 	}
-	ASSERT(i == xfs_iext_count(ifp));
+	ASSERT(ir.loaded == xfs_iext_count(ir.ifp));
 
-	ifp->if_flags |= XFS_IFEXTENTS;
+	ir.ifp->if_flags |= XFS_IFEXTENTS;
 	return 0;
-
-out_brelse:
-	xfs_trans_brelse(tp, bp);
 out:
-	xfs_iext_destroy(ifp);
+	xfs_iext_destroy(ir.ifp);
 	return error;
 }
 
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 71de937f9e64..8f2c151b543a 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -4286,6 +4286,7 @@  int
 xfs_btree_visit_blocks(
 	struct xfs_btree_cur		*cur,
 	xfs_btree_visit_blocks_fn	fn,
+	unsigned int			flags,
 	void				*data)
 {
 	union xfs_btree_ptr		lptr;
@@ -4313,6 +4314,9 @@  xfs_btree_visit_blocks(
 			xfs_btree_copy_ptrs(cur, &lptr, ptr, 1);
 		}
 
+		if ((flags & XFS_BTREE_VISIT_RECORDS_ONLY) && level > 0)
+			continue;
+
 		/* for each buffer in the level */
 		do {
 			error = xfs_btree_visit_block(cur, level, fn, data);
@@ -4412,7 +4416,7 @@  xfs_btree_change_owner(
 	bbcoi.new_owner = new_owner;
 	bbcoi.buffer_list = buffer_list;
 
-	return xfs_btree_visit_blocks(cur, xfs_btree_block_change_owner,
+	return xfs_btree_visit_blocks(cur, xfs_btree_block_change_owner, 0,
 			&bbcoi);
 }
 
@@ -4864,7 +4868,7 @@  xfs_btree_count_blocks(
 	xfs_extlen_t		*blocks)
 {
 	*blocks = 0;
-	return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper,
+	return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper, 0,
 			blocks);
 }
 
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index ced1e65d1483..d2fc17f84d9b 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -482,8 +482,10 @@  int xfs_btree_query_all(struct xfs_btree_cur *cur, xfs_btree_query_range_fn fn,
 
 typedef int (*xfs_btree_visit_blocks_fn)(struct xfs_btree_cur *cur, int level,
 		void *data);
+/* Only visit record blocks. */
+#define XFS_BTREE_VISIT_RECORDS_ONLY	(0x1)
 int xfs_btree_visit_blocks(struct xfs_btree_cur *cur,
-		xfs_btree_visit_blocks_fn fn, void *data);
+		xfs_btree_visit_blocks_fn fn, unsigned int flags, void *data);
 
 int xfs_btree_count_blocks(struct xfs_btree_cur *cur, xfs_extlen_t *blocks);
 
diff --git a/fs/xfs/scrub/bitmap.c b/fs/xfs/scrub/bitmap.c
index 3d47d111be5a..6e5e28fcdd4a 100644
--- a/fs/xfs/scrub/bitmap.c
+++ b/fs/xfs/scrub/bitmap.c
@@ -294,5 +294,6 @@  xfs_bitmap_set_btblocks(
 	struct xfs_bitmap	*bitmap,
 	struct xfs_btree_cur	*cur)
 {
-	return xfs_btree_visit_blocks(cur, xfs_bitmap_collect_btblock, bitmap);
+	return xfs_btree_visit_blocks(cur, xfs_bitmap_collect_btblock, 0,
+			bitmap);
 }