xfs: make AGFL repair function avoid crosslinked blocks
authorDarrick J. Wong <djwong@kernel.org>
Mon, 7 Nov 2022 01:03:14 +0000 (17:03 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 16 Nov 2022 23:25:01 +0000 (15:25 -0800)
Teach the AGFL repair function to check each block of the proposed AGFL
against the rmap btree.  If the rmapbt finds any mappings that are not
OWN_AG, strike that block from the list.

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

index 2e75ff9b5b2e92ee8dcd2ef28f0d414a4cb478ee..82ceb60ea5fcdbb4da96bd96fb4c9a85aa3d1244 100644 (file)
@@ -442,12 +442,18 @@ out_revert:
 /* AGFL */
 
 struct xrep_agfl {
+       /* Bitmap of alleged AGFL blocks that we're not going to add. */
+       struct xbitmap          crossed;
+
        /* Bitmap of other OWN_AG metadata blocks. */
        struct xbitmap          agmetablocks;
 
        /* Bitmap of free space. */
        struct xbitmap          *freesp;
 
+       /* rmapbt cursor for finding crosslinked blocks */
+       struct xfs_btree_cur    *rmap_cur;
+
        struct xfs_scrub        *sc;
 };
 
@@ -477,6 +483,41 @@ xrep_agfl_walk_rmap(
        return xbitmap_set_btcur_path(&ra->agmetablocks, cur);
 }
 
+/* Strike out the blocks that are cross-linked according to the rmapbt. */
+STATIC int
+xrep_agfl_check_extent(
+       struct xrep_agfl        *ra,
+       uint64_t                start,
+       uint64_t                len)
+{
+       xfs_agblock_t           agbno = XFS_FSB_TO_AGBNO(ra->sc->mp, start);
+       xfs_agblock_t           last_agbno = agbno + len - 1;
+       int                     error;
+
+       ASSERT(XFS_FSB_TO_AGNO(ra->sc->mp, start) == ra->sc->sa.pag->pag_agno);
+
+       while (agbno <= last_agbno) {
+               bool            other_owners;
+
+               error = xfs_rmap_has_other_keys(ra->rmap_cur, agbno, 1,
+                               &XFS_RMAP_OINFO_AG, &other_owners);
+               if (error)
+                       return error;
+
+               if (other_owners) {
+                       error = xbitmap_set(&ra->crossed, agbno, 1);
+                       if (error)
+                               return error;
+               }
+
+               if (xchk_should_terminate(ra->sc, &error))
+                       return error;
+               agbno++;
+       }
+
+       return 0;
+}
+
 /*
  * Map out all the non-AGFL OWN_AG space in this AG so that we can deduce
  * which blocks belong to the AGFL.
@@ -496,44 +537,58 @@ xrep_agfl_collect_blocks(
        struct xrep_agfl        ra;
        struct xfs_mount        *mp = sc->mp;
        struct xfs_btree_cur    *cur;
+       struct xbitmap_range    *br, *n;
        int                     error;
 
        ra.sc = sc;
        ra.freesp = agfl_extents;
        xbitmap_init(&ra.agmetablocks);
+       xbitmap_init(&ra.crossed);
 
        /* Find all space used by the free space btrees & rmapbt. */
        cur = xfs_rmapbt_init_cursor(mp, sc->tp, agf_bp, sc->sa.pag);
        error = xfs_rmap_query_all(cur, xrep_agfl_walk_rmap, &ra);
-       if (error)
-               goto err;
        xfs_btree_del_cursor(cur, error);
+       if (error)
+               goto out_bmp;
 
        /* Find all blocks currently being used by the bnobt. */
        cur = xfs_allocbt_init_cursor(mp, sc->tp, agf_bp,
                        sc->sa.pag, XFS_BTNUM_BNO);
        error = xbitmap_set_btblocks(&ra.agmetablocks, cur);
-       if (error)
-               goto err;
        xfs_btree_del_cursor(cur, error);
+       if (error)
+               goto out_bmp;
 
        /* Find all blocks currently being used by the cntbt. */
        cur = xfs_allocbt_init_cursor(mp, sc->tp, agf_bp,
                        sc->sa.pag, XFS_BTNUM_CNT);
        error = xbitmap_set_btblocks(&ra.agmetablocks, cur);
-       if (error)
-               goto err;
-
        xfs_btree_del_cursor(cur, error);
+       if (error)
+               goto out_bmp;
 
        /*
         * Drop the freesp meta blocks that are in use by btrees.
         * The remaining blocks /should/ be AGFL blocks.
         */
        error = xbitmap_disunion(agfl_extents, &ra.agmetablocks);
-       xbitmap_destroy(&ra.agmetablocks);
        if (error)
-               return error;
+               goto out_bmp;
+
+       /* Strike out the blocks that are cross-linked. */
+       ra.rmap_cur = xfs_rmapbt_init_cursor(mp, sc->tp, agf_bp, sc->sa.pag);
+       for_each_xbitmap_extent(br, n, agfl_extents) {
+               error = xrep_agfl_check_extent(&ra, br->start, br->len);
+               if (error)
+                       break;
+       }
+       xfs_btree_del_cursor(ra.rmap_cur, error);
+       if (error)
+               goto out_bmp;
+       error = xbitmap_disunion(agfl_extents, &ra.crossed);
+       if (error)
+               goto out_bmp;
 
        /*
         * Calculate the new AGFL size.  If we found more blocks than fit in
@@ -541,11 +596,10 @@ xrep_agfl_collect_blocks(
         */
        *flcount = min_t(uint64_t, xbitmap_hweight(agfl_extents),
                         xfs_agfl_size(mp));
-       return 0;
 
-err:
+out_bmp:
+       xbitmap_destroy(&ra.crossed);
        xbitmap_destroy(&ra.agmetablocks);
-       xfs_btree_del_cursor(cur, error);
        return error;
 }