xfs: flag refcount btree records that could be merged
authorDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:27 +0000 (19:00 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:27 +0000 (19:00 -0700)
Complain if we encounter refcount btree records that could be merged.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/scrub/refcount.c

index db9e46a..ed5eb36 100644 (file)
@@ -333,6 +333,9 @@ xchk_refcountbt_xref(
 }
 
 struct xchk_refcbt_records {
+       /* Previous refcount record. */
+       struct xfs_refcount_irec prev_rec;
+
        /* The next AG block where we aren't expecting shared extents. */
        xfs_agblock_t           next_unshared_agbno;
 
@@ -390,6 +393,46 @@ xchk_refcountbt_xref_gaps(
                xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur);
 }
 
+static inline bool
+xchk_refcount_mergeable(
+       struct xchk_refcbt_records      *rrc,
+       const struct xfs_refcount_irec  *r2)
+{
+       const struct xfs_refcount_irec  *r1 = &rrc->prev_rec;
+
+       /* Ignore if prev_rec is not yet initialized. */
+       if (r1->rc_blockcount > 0)
+               return false;
+
+       if (r1->rc_domain != r2->rc_domain)
+               return false;
+       if (r1->rc_startblock + r1->rc_blockcount != r2->rc_startblock)
+               return false;
+       if (r1->rc_refcount != r2->rc_refcount)
+               return false;
+       if ((unsigned long long)r1->rc_blockcount + r2->rc_blockcount >
+                       MAXREFCEXTLEN)
+               return false;
+
+       return true;
+}
+
+/* Flag failures for records that could be merged. */
+STATIC void
+xchk_refcountbt_check_mergeable(
+       struct xchk_btree               *bs,
+       struct xchk_refcbt_records      *rrc,
+       const struct xfs_refcount_irec  *irec)
+{
+       if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+               return;
+
+       if (xchk_refcount_mergeable(rrc, irec))
+               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+
+       memcpy(&rrc->prev_rec, irec, sizeof(struct xfs_refcount_irec));
+}
+
 /* Scrub a refcountbt record. */
 STATIC int
 xchk_refcountbt_rec(
@@ -414,6 +457,7 @@ xchk_refcountbt_rec(
                xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
        rrc->prev_domain = irec.rc_domain;
 
+       xchk_refcountbt_check_mergeable(bs, rrc, &irec);
        xchk_refcountbt_xref(bs->sc, &irec);
 
        /*