Re: [PATCH] xfs: Allow scrub to detect inodes with non-maximal sized extents

Darrick J. Wong
2021-02-23T17:03:06Z

On Tue, Feb 23, 2021 at 01:56:29PM +0530, Chandan Babu R wrote:
> This commit now makes it possible for scrub to check if an inode's extents are
> maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
> both file offset and disk offset) with neighbouring extents and the total
> length of both the extents is less than the maximum allowed extent
> length (i.e. MAXEXTLEN).
>
> Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
> ---
> fs/xfs/scrub/inode.c | 11 +++++++----
> fs/xfs/xfs_bmap_util.c | 36 +++++++++++++++++++++++++++++++-----
> fs/xfs/xfs_bmap_util.h | 5 +++--
> fs/xfs/xfs_qm.c | 2 +-
> 4 files changed, 42 insertions(+), 12 deletions(-)
>
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index faf65eb5bd31..33b530785e36 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -478,23 +478,26 @@ xchk_inode_xref_bmap(
> xfs_filblks_t count;
> xfs_filblks_t acount;
> int error;
> + bool maximal_sized_exts;
>
> if (xchk_skip_xref(sc->sm))
> return;
>
> /* Walk all the extents to check nextents/naextents/nblocks. */
> + maximal_sized_exts = true;

Shouldn't this be set by xfs_bmap_count_blocks()? It's purely an out
parameter.

> error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
> - &nextents, &count);
> + &nextents, &count, &maximal_sized_exts);
> if (!xchk_should_check_xref(sc, &error, NULL))
> return;
> - if (nextents < be32_to_cpu(dip->di_nextents))
> + if (nextents < be32_to_cpu(dip->di_nextents) || !maximal_sized_exts)
> xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>
> + maximal_sized_exts = true;
> error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
> - &nextents, &acount);
> + &nextents, &acount, &maximal_sized_exts);
> if (!xchk_should_check_xref(sc, &error, NULL))
> return;
> - if (nextents != be16_to_cpu(dip->di_anextents))
> + if (nextents != be16_to_cpu(dip->di_anextents) || !maximal_sized_exts)
> xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>
> /* Check nblocks against the inode. */
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index e7d68318e6a5..eca39677a46f 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -176,6 +176,25 @@ xfs_bmap_rtalloc(
> * Extent tree block counting routines.
> */
>
> +STATIC bool
> +is_maximal_extent(
> + struct xfs_ifork *ifp,
> + struct xfs_iext_cursor *icur,
> + struct xfs_bmbt_irec *got)
> +{
> + struct xfs_bmbt_irec left;
> +
> + if (xfs_iext_peek_prev_extent(ifp, icur, &left) &&
> + !isnullstartblock(left.br_startblock) &&
> + left.br_startoff + left.br_blockcount == got->br_startoff &&
> + left.br_startblock + left.br_blockcount == got->br_startblock &&
> + left.br_state == got->br_state &&
> + left.br_blockcount + got->br_blockcount <= MAXEXTLEN)
> + return false;

Please don't align the if test code with the statement body.

> + else

No need for the else here.

> + return true;
> +}
> +
> /*
> * Count leaf blocks given a range of extent records. Delayed allocation
> * extents are not counted towards the totals.
> @@ -183,7 +202,8 @@ xfs_bmap_rtalloc(
> xfs_extnum_t
> xfs_bmap_count_leaves(
> struct xfs_ifork *ifp,
> - xfs_filblks_t *count)
> + xfs_filblks_t *count,
> + bool *maximal_sized_exts)
> {
> struct xfs_iext_cursor icur;
> struct xfs_bmbt_irec got;
> @@ -191,6 +211,10 @@ xfs_bmap_count_leaves(
>
> for_each_xfs_iext(ifp, &icur, &got) {
> if (!isnullstartblock(got.br_startblock)) {
> + if (maximal_sized_exts)
> + *maximal_sized_exts =
> + is_maximal_extent(ifp, &icur, &got);

Shouldn't this be *maximal_sized_exts &= is_maximal_extent(); ?

Otherwise this only tells us if the last extent in the fork is maximal.

I also wonder if it's really worth the extra complexity to make the
boolean an optional argument, since there are callers that pass in a
count pointer that's a dummy value.

--D

> +
> *count += got.br_blockcount;
> numrecs++;
> }
> @@ -209,7 +233,8 @@ xfs_bmap_count_blocks(
> struct xfs_inode *ip,
> int whichfork,
> xfs_extnum_t *nextents,
> - xfs_filblks_t *count)
> + xfs_filblks_t *count,
> + bool *maximal_sized_exts)
> {
> struct xfs_mount *mp = ip->i_mount;
> struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
> @@ -246,7 +271,8 @@ xfs_bmap_count_blocks(
>
> /* fall through */
> case XFS_DINODE_FMT_EXTENTS:
> - *nextents = xfs_bmap_count_leaves(ifp, count);
> + *nextents = xfs_bmap_count_leaves(ifp, count,
> + maximal_sized_exts);
> break;
> }
>
> @@ -1442,14 +1468,14 @@ xfs_swap_extent_forks(
> if (XFS_IFORK_Q(ip) && ip->i_afp->if_nextents > 0 &&
> ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
> error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk,
> - &aforkblks);
> + &aforkblks, NULL);
> if (error)
> return error;
> }
> if (XFS_IFORK_Q(tip) && tip->i_afp->if_nextents > 0 &&
> tip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
> error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk,
> - &taforkblks);
> + &taforkblks, NULL);
> if (error)
> return error;
> }
> diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
> index 9f993168b55b..7b0ce227c7bd 100644
> --- a/fs/xfs/xfs_bmap_util.h
> +++ b/fs/xfs/xfs_bmap_util.h
> @@ -71,10 +71,11 @@ int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
>
> xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
>
> -xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
> +xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count,
> + bool *maximal_sized_exts);
> int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
> int whichfork, xfs_extnum_t *nextents,
> - xfs_filblks_t *count);
> + xfs_filblks_t *count, bool *maximal_sized_exts);
>
> int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
> xfs_off_t len);
> diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
> index 742d1413e2d0..04fcca5583b3 100644
> --- a/fs/xfs/xfs_qm.c
> +++ b/fs/xfs/xfs_qm.c
> @@ -1170,7 +1170,7 @@ xfs_qm_dqusage_adjust(
> goto error0;
> }
>
> - xfs_bmap_count_leaves(ifp, &rtblks);
> + xfs_bmap_count_leaves(ifp, &rtblks, NULL);
> }
>
> nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks;
> --
> 2.29.2
>