Re: [PATCH 2/6] xfs: xrep_findroot_block should reject root blocks with siblings

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

 



On Sat, Aug 11, 2018 at 08:35:09AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
> 
> In xrep_findroot_block, if we find a candidate root block with sibling
> pointers or sibling blocks on the same tree level, we should not return
> that block as a tree root because root blocks cannot have siblings.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
> ---
>  fs/xfs/scrub/repair.c |   47 +++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 6 deletions(-)
> 
> 
> diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c
> index 17cf48564390..42d8c798ce7d 100644
> --- a/fs/xfs/scrub/repair.c
> +++ b/fs/xfs/scrub/repair.c
> @@ -690,6 +690,7 @@ xrep_findroot_block(
>  	struct xfs_buf			*bp;
>  	struct xfs_btree_block		*btblock;
>  	xfs_daddr_t			daddr;
> +	int				block_level;
>  	int				error;
>  
>  	daddr = XFS_AGB_TO_DADDR(mp, ri->sc->sa.agno, agbno);
> @@ -727,17 +728,51 @@ xrep_findroot_block(
>  		goto out;
>  	bp->b_ops = fab->buf_ops;
>  
> -	/* Ignore this block if it's lower in the tree than we've seen. */
> -	if (fab->root != NULLAGBLOCK &&
> -	    xfs_btree_get_level(btblock) < fab->height)
> -		goto out;
> -
>  	/* Make sure we pass the verifiers. */
>  	bp->b_ops->verify_read(bp);
>  	if (bp->b_error)
>  		goto out;
> +
> +	/* If we've recorded a root candidate... */
> +	block_level = xfs_btree_get_level(btblock);
> +	if (fab->root != NULLAGBLOCK) {
> +		/*
> +		 * ...and this no-sibling root block candidate has the same
> +		 * level as the recorded candidate, there's no way we're going
> +		 * to accept any candidates at this tree level.  Stash a root
> +		 * block of zero because the height is still valid, but no
> +		 * AG btree can root at agblock 0.  Callers should verify the
> +		 * root agbno with xfs_verify_agbno...
> +		 */
> +		if (block_level + 1 == fab->height) {
> +			fab->root = 0;
> +			goto out;
> +		}
> +
> +		/*
> +		 * ...and this no-sibling root block is lower in the tree than
> +		 * the recorded root block candidate, just ignore it.  There's
> +		 * still a strong chance that something is wrong with the btree
> +		 * itself, but that's not what we're fixing right now.
> +		 */
> +		if (block_level < fab->height)
> +			goto out;
> +	}
> +
> +	/*
> +	 * Root blocks can't have siblings.  This level can't be the root, so
> +	 * record the tree height (but not the ag block pointer) to force us to
> +	 * look for a higher level in the tree.
> +	 */
> +	if (btblock->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) ||
> +	    btblock->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)) {
> +		fab->root = 0;
> +		fab->height = block_level + 1;
> +		goto out;
> +	}
> +

Hmm, this looks technically correct but this still seems more involved
than necessary. I don't really like how we define yet another magic
value of 0, for example, and differentiate that from NULLAGBLOCK when it
seems like we could just check the height.

Could we do something like the following (which assumes ->height is
initialized to 0)?

        /*
         * If current height exceeds the block level, we've already seen at
         * least one block at this level or higher. Skip it and invalidate the
         * root if this block matches the current root level because a root
         * block has no siblings.
         */
        block_level = xfs_btree_get_level(btblock);
        if (fab->height > block_level) {
                if (fab->height - 1 == block_level)
                        fab->root = NULLAGBLOCK;
                goto out;
        }

        /*
         * Found a block with a new max height. Track it as a root candidate if
         * it has no siblings. Otherwise invalidate root since we know the root
         * can't be at this level.
         */
        if (btblock->bb_u.s.bb_leftsib == cpu_to_be32(NULLAGBLOCK) &&
            btblock->bb_u.s.bb_rightsib == cpu_to_be32(NULLAGBLOCK))
                fab->root = agbno;
        else
                fab->root = NULLAGBLOCK;
        fab->height = block_level + 1;

... and then ->root should either be valid or NULLAGBLOCK at the end..?

Also, shouldn't we set *found_it a bit earlier in this function? It
looks like that simply controls the fab iteration and we have
technically matched the block to a btree type at this point, regardless
of whether we update root/height. (I wonder if something like "matched"
or "found_match" might be a better name than found_it...).

Brian

>  	fab->root = agbno;
> -	fab->height = xfs_btree_get_level(btblock) + 1;
> +	fab->height = block_level + 1;
>  	*found_it = true;
>  
>  	trace_xrep_findroot_block(mp, ri->sc->sa.agno, agbno,
> 



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux