xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 17 Jan 2018 02:53:05 +0000 (18:53 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Thu, 18 Jan 2018 05:00:45 +0000 (21:00 -0800)
When scanning a metadata btree block, cross-reference the block location
with the free space btree and the reverse mapping btree to ensure that
the rmapbt knows about the block and the bnobt does not.  Add a
mechanism to defer checks when we happen to be scanning the bnobt/rmapbt
itself because it's less efficient to repeatedly clone and destroy the
cursor.

This patch provides the framework to make btree block owner checks
happen; the actual meat will be added in subsequent patches.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/scrub/btree.c

index 4751ed03b9fba7c78e6c82f2796e6c8aa0fc3b0b..e671d694908bd57377c314f60ab256cd1825ee16 100644 (file)
@@ -361,6 +361,80 @@ out:
        return error;
 }
 
+struct check_owner {
+       struct list_head        list;
+       xfs_daddr_t             daddr;
+       int                     level;
+};
+
+/*
+ * Make sure this btree block isn't in the free list and that there's
+ * an rmap record for it.
+ */
+STATIC int
+xfs_scrub_btree_check_block_owner(
+       struct xfs_scrub_btree          *bs,
+       int                             level,
+       xfs_daddr_t                     daddr)
+{
+       xfs_agnumber_t                  agno;
+       bool                            init_sa;
+       int                             error = 0;
+
+       if (!bs->cur)
+               return 0;
+
+       agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
+
+       init_sa = bs->cur->bc_flags & XFS_BTREE_LONG_PTRS;
+       if (init_sa) {
+               error = xfs_scrub_ag_init(bs->sc, agno, &bs->sc->sa);
+               if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur,
+                               level, &error))
+                       return error;
+       }
+
+       if (init_sa)
+               xfs_scrub_ag_free(bs->sc, &bs->sc->sa);
+
+       return error;
+}
+
+/* Check the owner of a btree block. */
+STATIC int
+xfs_scrub_btree_check_owner(
+       struct xfs_scrub_btree          *bs,
+       int                             level,
+       struct xfs_buf                  *bp)
+{
+       struct xfs_btree_cur            *cur = bs->cur;
+       struct check_owner              *co;
+
+       if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
+               return 0;
+
+       /*
+        * We want to cross-reference each btree block with the bnobt
+        * and the rmapbt.  We cannot cross-reference the bnobt or
+        * rmapbt while scanning the bnobt or rmapbt, respectively,
+        * because we cannot alter the cursor and we'd prefer not to
+        * duplicate cursors.  Therefore, save the buffer daddr for
+        * later scanning.
+        */
+       if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
+               co = kmem_alloc(sizeof(struct check_owner),
+                               KM_MAYFAIL | KM_NOFS);
+               if (!co)
+                       return -ENOMEM;
+               co->level = level;
+               co->daddr = XFS_BUF_ADDR(bp);
+               list_add_tail(&co->list, &bs->to_check);
+               return 0;
+       }
+
+       return xfs_scrub_btree_check_block_owner(bs, level, XFS_BUF_ADDR(bp));
+}
+
 /*
  * Grab and scrub a btree block given a btree pointer.  Returns block
  * and buffer pointers (if applicable) if they're ok to use.
@@ -396,6 +470,14 @@ xfs_scrub_btree_get_block(
                return 0;
        }
 
+       /*
+        * Check the block's owner; this function absorbs error codes
+        * for us.
+        */
+       error = xfs_scrub_btree_check_owner(bs, level, *pbp);
+       if (error)
+               return error;
+
        /*
         * Check the block's siblings; this function absorbs error codes
         * for us.
@@ -467,6 +549,8 @@ xfs_scrub_btree(
        struct xfs_btree_block          *block;
        int                             level;
        struct xfs_buf                  *bp;
+       struct check_owner              *co;
+       struct check_owner              *n;
        int                             i;
        int                             error = 0;
 
@@ -558,5 +642,14 @@ xfs_scrub_btree(
        }
 
 out:
+       /* Process deferred owner checks on btree blocks. */
+       list_for_each_entry_safe(co, n, &bs.to_check, list) {
+               if (!error && bs.cur)
+                       error = xfs_scrub_btree_check_block_owner(&bs,
+                                       co->level, co->daddr);
+               list_del(&co->list);
+               kmem_free(co);
+       }
+
        return error;
 }