Commit e992ae8a authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: refactor xfs_iread_extents to use xfs_btree_visit_blocks



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: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent fec40e22
Loading
Loading
Loading
Loading
+75 −112
Original line number Diff line number Diff line
@@ -1155,143 +1155,106 @@ xfs_bmap_add_attrfork(
 * Internal and external extent tree search functions.
 */

/*
 * Read in extents from a btree-format inode.
 */
int
xfs_iread_extents(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	int			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_iread_state {
	struct xfs_iext_cursor	icur;
	struct xfs_bmbt_irec	new;
	xfs_fsblock_t		bno;
	xfs_extnum_t		loaded;
};

/* 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;
	xfs_extnum_t		i, j;
	int			level;
	__be64			*pp;
	int			error;
	struct xfs_bmbt_rec	*frp;
	xfs_extnum_t		num_recs;
	xfs_extnum_t		j;
	int			whichfork = cur->bc_private.b.whichfork;

	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
	block = xfs_btree_get_block(cur, level, &bp);

	if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
	/* Abort if we find more records than nextents. */
	num_recs = xfs_btree_get_numrecs(block);
	if (unlikely(ir->loaded + num_recs >
		     XFS_IFORK_NEXTENTS(ip, whichfork))) {
		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;
	}

	/*
	 * 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);
	/* 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;
		}
	pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
	bno = be64_to_cpu(*pp);
		xfs_iext_insert(ip, &ir->icur, &new,
				xfs_bmap_fork_to_state(whichfork));
		trace_xfs_read_extent(ip, &ir->icur,
				xfs_bmap_fork_to_state(whichfork), _THIS_IP_);
		xfs_iext_next(XFS_IFORK_PTR(ip, whichfork), &ir->icur);
	}

	/*
	 * 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);
	return 0;
}

/*
	 * Here with bp and block set to the leftmost leaf node in the tree.
 * Read in extents from a btree-format inode.
 */
	i = 0;
	xfs_iext_first(ifp, &icur);
int
xfs_iread_extents(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	int			whichfork)
{
	struct xfs_iread_state	ir;
	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_btree_cur	*cur;
	int			error;

	/*
	 * 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;
	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

		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);
	if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
		error = -EFSCORRUPTED;
			goto out_brelse;
		goto out;
	}
		/*
		 * 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);
	ir.loaded = 0;
	xfs_iext_first(ifp, &ir.icur);
	cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
	error = xfs_btree_visit_blocks(cur, xfs_iread_bmbt_block,
			XFS_BTREE_VISIT_RECORDS, &ir);
	xfs_btree_del_cursor(cur, error);
	if (error)
		goto out;
		block = XFS_BUF_TO_BLOCK(bp);
	}

	if (i != XFS_IFORK_NEXTENTS(ip, whichfork)) {
	if (ir.loaded != XFS_IFORK_NEXTENTS(ip, whichfork)) {
		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
		error = -EFSCORRUPTED;
		goto out;
	}
	ASSERT(i == xfs_iext_count(ifp));
	ASSERT(ir.loaded == xfs_iext_count(ifp));

	ifp->if_flags |= XFS_IFEXTENTS;
	return 0;

out_brelse:
	xfs_trans_brelse(tp, bp);
out:
	xfs_iext_destroy(ifp);
	return error;
+8 −2
Original line number Diff line number Diff line
@@ -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;
@@ -4311,6 +4312,11 @@ xfs_btree_visit_blocks(

			/* save for the next iteration of the loop */
			xfs_btree_copy_ptrs(cur, &lptr, ptr, 1);

			if (!(flags & XFS_BTREE_VISIT_LEAVES))
				continue;
		} else if (!(flags & XFS_BTREE_VISIT_RECORDS)) {
			continue;
		}

		/* for each buffer in the level */
@@ -4413,7 +4419,7 @@ xfs_btree_change_owner(
	bbcoi.buffer_list = buffer_list;

	return xfs_btree_visit_blocks(cur, xfs_btree_block_change_owner,
			&bbcoi);
			XFS_BTREE_VISIT_ALL, &bbcoi);
}

/* Verify the v5 fields of a long-format btree block. */
@@ -4865,7 +4871,7 @@ xfs_btree_count_blocks(
{
	*blocks = 0;
	return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper,
			blocks);
			XFS_BTREE_VISIT_ALL, blocks);
}

/* Compare two btree pointers. */
+8 −1
Original line number Diff line number Diff line
@@ -485,8 +485,15 @@ 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);
/* Visit record blocks. */
#define XFS_BTREE_VISIT_RECORDS		(1 << 0)
/* Visit leaf blocks. */
#define XFS_BTREE_VISIT_LEAVES		(1 << 1)
/* Visit all blocks. */
#define XFS_BTREE_VISIT_ALL		(XFS_BTREE_VISIT_RECORDS | \
					 XFS_BTREE_VISIT_LEAVES)
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);

+2 −1
Original line number Diff line number Diff line
@@ -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,
			XFS_BTREE_VISIT_ALL, bitmap);
}