]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: scrub the realtime refcount btree
authorDarrick J. Wong <djwong@kernel.org>
Tue, 15 Oct 2024 19:40:32 +0000 (12:40 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:36:33 +0000 (13:36 -0800)
Add code to scrub realtime refcount btrees.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
13 files changed:
fs/xfs/Makefile
fs/xfs/libxfs/xfs_fs.h
fs/xfs/scrub/bmap.c
fs/xfs/scrub/bmap_repair.c
fs/xfs/scrub/common.c
fs/xfs/scrub/common.h
fs/xfs/scrub/health.c
fs/xfs/scrub/inode.c
fs/xfs/scrub/rtrefcount.c [new file with mode: 0644]
fs/xfs/scrub/scrub.c
fs/xfs/scrub/scrub.h
fs/xfs/scrub/stats.c
fs/xfs/scrub/trace.h

index b9356d01416e1604239552de4dcd7e997f22d786..9dd9921e53567c78b0f5d6f15f15e2396dd7022a 100644 (file)
@@ -195,6 +195,7 @@ xfs-$(CONFIG_XFS_ONLINE_SCRUB_STATS) += scrub/stats.o
 xfs-$(CONFIG_XFS_RT)           += $(addprefix scrub/, \
                                   rgsuper.o \
                                   rtbitmap.o \
+                                  rtrefcount.o \
                                   rtrmap.o \
                                   rtsummary.o \
                                   )
index ea9e58a89d92d3260fb0e10bedcc4cdd8603d487..a4bd6a39c6ba710e6e96d1030c8569f5ffc89844 100644 (file)
@@ -738,9 +738,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_METAPATH        29      /* metadata directory tree paths */
 #define XFS_SCRUB_TYPE_RGSUPER 30      /* realtime superblock */
 #define XFS_SCRUB_TYPE_RTRMAPBT        31      /* rtgroup reverse mapping btree */
+#define XFS_SCRUB_TYPE_RTREFCBT        32      /* realtime reference count btree */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR      32
+#define XFS_SCRUB_TYPE_NR      33
 
 /*
  * This special type code only applies to the vectored scrub implementation.
index 2f78a4e23659180841709384b5419fca6a5e8c52..9fa99f3d222f79b683c05765524b8fe3ef187585 100644 (file)
@@ -1047,6 +1047,7 @@ xchk_bmap(
        case XFS_DINODE_FMT_DEV:
        case XFS_DINODE_FMT_LOCAL:
        case XFS_DINODE_FMT_RMAP:
+       case XFS_DINODE_FMT_REFCOUNT:
                /* No mappings to check. */
                if (whichfork == XFS_COW_FORK)
                        xchk_fblock_set_corrupt(sc, whichfork, 0);
index fcc70a325f07d05608c3077b74ba978ccb5a8122..5bf56afe0139467d95479990086b70549801649c 100644 (file)
@@ -856,6 +856,7 @@ xrep_bmap_check_inputs(
        case XFS_DINODE_FMT_LOCAL:
        case XFS_DINODE_FMT_UUID:
        case XFS_DINODE_FMT_RMAP:
+       case XFS_DINODE_FMT_REFCOUNT:
                return -ECANCELED;
        case XFS_DINODE_FMT_EXTENTS:
        case XFS_DINODE_FMT_BTREE:
index 7ebc22782c492df6ea7b7fc5297b7d5037c71206..299c9f841ffafdc221777554ce14840b3042bf94 100644 (file)
@@ -37,6 +37,7 @@
 #include "xfs_rtgroup.h"
 #include "xfs_rtrmap_btree.h"
 #include "xfs_bmap_util.h"
+#include "xfs_rtrefcount_btree.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
@@ -797,6 +798,9 @@ xchk_rtgroup_lock(
        if (xfs_has_rtrmapbt(sc->mp) && (rtglock_flags & XFS_RTGLOCK_RMAP))
                sr->rmap_cur = xfs_rtrmapbt_init_cursor(sc->tp, sr->rtg);
 
+       if (xfs_has_rtreflink(sc->mp) && (rtglock_flags & XFS_RTGLOCK_REFCOUNT))
+               sr->refc_cur = xfs_rtrefcountbt_init_cursor(sc->tp, sr->rtg);
+
        return 0;
 }
 
@@ -811,7 +815,10 @@ xchk_rtgroup_btcur_free(
 {
        if (sr->rmap_cur)
                xfs_btree_del_cursor(sr->rmap_cur, XFS_BTREE_ERROR);
+       if (sr->refc_cur)
+               xfs_btree_del_cursor(sr->refc_cur, XFS_BTREE_ERROR);
 
+       sr->refc_cur = NULL;
        sr->rmap_cur = NULL;
 }
 
@@ -1698,16 +1705,25 @@ xchk_inode_count_blocks(
                        return -EFSCORRUPTED;
                }
                cur = xfs_rtrmapbt_init_cursor(sc->tp, sc->sr.rtg);
-               error = xfs_btree_count_blocks(cur, &btblocks);
-               xfs_btree_del_cursor(cur, error);
-               if (error)
-                       return error;
-
-               *nextents = 0;
-               *count = btblocks - 1;
-               return 0;
-       default:
-               return xfs_bmap_count_blocks(sc->tp, sc->ip, whichfork,
-                               nextents, count);
+               goto meta_btree;
+       case XFS_DINODE_FMT_REFCOUNT:
+               if (!sc->sr.rtg) {
+                       ASSERT(0);
+                       return -EFSCORRUPTED;
+               }
+               cur = xfs_rtrefcountbt_init_cursor(sc->tp, sc->sr.rtg);
+               goto meta_btree;
        }
+
+       return xfs_bmap_count_blocks(sc->tp, sc->ip, whichfork, nextents,
+                       count);
+meta_btree:
+       error = xfs_btree_count_blocks(cur, &btblocks);
+       xfs_btree_del_cursor(cur, error);
+       if (error)
+               return error;
+
+       *nextents = 0;
+       *count = btblocks - 1;
+       return 0;
 }
index 50ac6cca18fe45b52eb26b07e276e1e7a993e0ed..bdcd40f0ec742cba05d23d06e5277d233dc97765 100644 (file)
@@ -82,11 +82,13 @@ int xchk_setup_rtbitmap(struct xfs_scrub *sc);
 int xchk_setup_rtsummary(struct xfs_scrub *sc);
 int xchk_setup_rgsuperblock(struct xfs_scrub *sc);
 int xchk_setup_rtrmapbt(struct xfs_scrub *sc);
+int xchk_setup_rtrefcountbt(struct xfs_scrub *sc);
 #else
 # define xchk_setup_rtbitmap           xchk_setup_nothing
 # define xchk_setup_rtsummary          xchk_setup_nothing
 # define xchk_setup_rgsuperblock       xchk_setup_nothing
 # define xchk_setup_rtrmapbt           xchk_setup_nothing
+# define xchk_setup_rtrefcountbt       xchk_setup_nothing
 #endif
 #ifdef CONFIG_XFS_QUOTA
 int xchk_ino_dqattach(struct xfs_scrub *sc);
@@ -129,7 +131,8 @@ xchk_ag_init_existing(
 
 /* All the locks we need to check an rtgroup. */
 #define XCHK_RTGLOCK_ALL       (XFS_RTGLOCK_BITMAP | \
-                                XFS_RTGLOCK_RMAP)
+                                XFS_RTGLOCK_RMAP | \
+                                XFS_RTGLOCK_REFCOUNT)
 
 int xchk_rtgroup_init(struct xfs_scrub *sc, xfs_rgnumber_t rgno,
                struct xchk_rt *sr);
index 13ef5632272b0f0c072c6e213dd5fab5746c1645..860e913f014b15f1ca1a612c346d1f69724f328d 100644 (file)
@@ -113,6 +113,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
        [XFS_SCRUB_TYPE_METAPATH]       = { XHG_FS,  XFS_SICK_FS_METAPATH },
        [XFS_SCRUB_TYPE_RGSUPER]        = { XHG_RTGROUP, XFS_SICK_RG_SUPER },
        [XFS_SCRUB_TYPE_RTRMAPBT]       = { XHG_RTGROUP, XFS_SICK_RG_RMAPBT },
+       [XFS_SCRUB_TYPE_RTREFCBT]       = { XHG_RTGROUP, XFS_SICK_RG_REFCNTBT },
 };
 
 /* Return the health status mask for this scrub type. */
index 162159dd21dbd73efb5d64357353d2c675e3fd91..9cd64a4fa1ae3d63c0f4bdfb196b94fe7cb83b9c 100644 (file)
@@ -504,6 +504,7 @@ xchk_dinode(
                        xchk_ino_set_corrupt(sc, ino);
                break;
        case XFS_DINODE_FMT_RMAP:
+       case XFS_DINODE_FMT_REFCOUNT:
                if (!S_ISREG(mode))
                        xchk_ino_set_corrupt(sc, ino);
                break;
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
new file mode 100644 (file)
index 0000000..54a85a6
--- /dev/null
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_refcount.h"
+#include "xfs_inode.h"
+#include "xfs_rtbitmap.h"
+#include "xfs_rtgroup.h"
+#include "xfs_metafile.h"
+#include "xfs_rtrefcount_btree.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/btree.h"
+
+/* Set us up with the realtime refcount metadata locked. */
+int
+xchk_setup_rtrefcountbt(
+       struct xfs_scrub        *sc)
+{
+       int                     error;
+
+       if (xchk_need_intent_drain(sc))
+               xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
+
+       error = xchk_rtgroup_init(sc, sc->sm->sm_agno, &sc->sr);
+       if (error)
+               return error;
+
+       error = xchk_setup_rt(sc);
+       if (error)
+               return error;
+
+       error = xchk_install_live_inode(sc,
+                       sc->sr.rtg->rtg_inodes[XFS_RTGI_REFCOUNT]);
+       if (error)
+               return error;
+
+       return xchk_rtgroup_lock(sc, &sc->sr, XCHK_RTGLOCK_ALL);
+}
+
+/* Realtime Reference count btree scrubber. */
+
+/*
+ * Confirming Reference Counts via Reverse Mappings
+ *
+ * We want to count the reverse mappings overlapping a refcount record
+ * (bno, len, refcount), allowing for the possibility that some of the
+ * overlap may come from smaller adjoining reverse mappings, while some
+ * comes from single extents which overlap the range entirely.  The
+ * outer loop is as follows:
+ *
+ * 1. For all reverse mappings overlapping the refcount extent,
+ *    a. If a given rmap completely overlaps, mark it as seen.
+ *    b. Otherwise, record the fragment (in agbno order) for later
+ *       processing.
+ *
+ * Once we've seen all the rmaps, we know that for all blocks in the
+ * refcount record we want to find $refcount owners and we've already
+ * visited $seen extents that overlap all the blocks.  Therefore, we
+ * need to find ($refcount - $seen) owners for every block in the
+ * extent; call that quantity $target_nr.  Proceed as follows:
+ *
+ * 2. Pull the first $target_nr fragments from the list; all of them
+ *    should start at or before the start of the extent.
+ *    Call this subset of fragments the working set.
+ * 3. Until there are no more unprocessed fragments,
+ *    a. Find the shortest fragments in the set and remove them.
+ *    b. Note the block number of the end of these fragments.
+ *    c. Pull the same number of fragments from the list.  All of these
+ *       fragments should start at the block number recorded in the
+ *       previous step.
+ *    d. Put those fragments in the set.
+ * 4. Check that there are $target_nr fragments remaining in the list,
+ *    and that they all end at or beyond the end of the refcount extent.
+ *
+ * If the refcount is correct, all the check conditions in the algorithm
+ * should always hold true.  If not, the refcount is incorrect.
+ */
+struct xchk_rtrefcnt_frag {
+       struct list_head        list;
+       struct xfs_rmap_irec    rm;
+};
+
+struct xchk_rtrefcnt_check {
+       struct xfs_scrub        *sc;
+       struct list_head        fragments;
+
+       /* refcount extent we're examining */
+       xfs_rgblock_t           bno;
+       xfs_extlen_t            len;
+       xfs_nlink_t             refcount;
+
+       /* number of owners seen */
+       xfs_nlink_t             seen;
+};
+
+/*
+ * Decide if the given rmap is large enough that we can redeem it
+ * towards refcount verification now, or if it's a fragment, in
+ * which case we'll hang onto it in the hopes that we'll later
+ * discover that we've collected exactly the correct number of
+ * fragments as the rtrefcountbt says we should have.
+ */
+STATIC int
+xchk_rtrefcountbt_rmap_check(
+       struct xfs_btree_cur            *cur,
+       const struct xfs_rmap_irec      *rec,
+       void                            *priv)
+{
+       struct xchk_rtrefcnt_check      *refchk = priv;
+       struct xchk_rtrefcnt_frag       *frag;
+       xfs_rgblock_t                   rm_last;
+       xfs_rgblock_t                   rc_last;
+       int                             error = 0;
+
+       if (xchk_should_terminate(refchk->sc, &error))
+               return error;
+
+       rm_last = rec->rm_startblock + rec->rm_blockcount - 1;
+       rc_last = refchk->bno + refchk->len - 1;
+
+       /* Confirm that a single-owner refc extent is a CoW stage. */
+       if (refchk->refcount == 1 && rec->rm_owner != XFS_RMAP_OWN_COW) {
+               xchk_btree_xref_set_corrupt(refchk->sc, cur, 0);
+               return 0;
+       }
+
+       if (rec->rm_startblock <= refchk->bno && rm_last >= rc_last) {
+               /*
+                * The rmap overlaps the refcount record, so we can confirm
+                * one refcount owner seen.
+                */
+               refchk->seen++;
+       } else {
+               /*
+                * This rmap covers only part of the refcount record, so
+                * save the fragment for later processing.  If the rmapbt
+                * is healthy each rmap_irec we see will be in agbno order
+                * so we don't need insertion sort here.
+                */
+               frag = kmalloc(sizeof(struct xchk_rtrefcnt_frag),
+                               XCHK_GFP_FLAGS);
+               if (!frag)
+                       return -ENOMEM;
+               memcpy(&frag->rm, rec, sizeof(frag->rm));
+               list_add_tail(&frag->list, &refchk->fragments);
+       }
+
+       return 0;
+}
+
+/*
+ * Given a bunch of rmap fragments, iterate through them, keeping
+ * a running tally of the refcount.  If this ever deviates from
+ * what we expect (which is the rtrefcountbt's refcount minus the
+ * number of extents that totally covered the rtrefcountbt extent),
+ * we have a rtrefcountbt error.
+ */
+STATIC void
+xchk_rtrefcountbt_process_rmap_fragments(
+       struct xchk_rtrefcnt_check      *refchk)
+{
+       struct list_head                worklist;
+       struct xchk_rtrefcnt_frag       *frag;
+       struct xchk_rtrefcnt_frag       *n;
+       xfs_rgblock_t                   bno;
+       xfs_rgblock_t                   rbno;
+       xfs_rgblock_t                   next_rbno;
+       xfs_nlink_t                     nr;
+       xfs_nlink_t                     target_nr;
+
+       target_nr = refchk->refcount - refchk->seen;
+       if (target_nr == 0)
+               return;
+
+       /*
+        * There are (refchk->rc.rc_refcount - refchk->nr refcount)
+        * references we haven't found yet.  Pull that many off the
+        * fragment list and figure out where the smallest rmap ends
+        * (and therefore the next rmap should start).  All the rmaps
+        * we pull off should start at or before the beginning of the
+        * refcount record's range.
+        */
+       INIT_LIST_HEAD(&worklist);
+       rbno = NULLRGBLOCK;
+
+       /* Make sure the fragments actually /are/ in bno order. */
+       bno = 0;
+       list_for_each_entry(frag, &refchk->fragments, list) {
+               if (frag->rm.rm_startblock < bno)
+                       goto done;
+               bno = frag->rm.rm_startblock;
+       }
+
+       /*
+        * Find all the rmaps that start at or before the refc extent,
+        * and put them on the worklist.
+        */
+       nr = 0;
+       list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+               if (frag->rm.rm_startblock > refchk->bno || nr > target_nr)
+                       break;
+               bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+               if (bno < rbno)
+                       rbno = bno;
+               list_move_tail(&frag->list, &worklist);
+               nr++;
+       }
+
+       /*
+        * We should have found exactly $target_nr rmap fragments starting
+        * at or before the refcount extent.
+        */
+       if (nr != target_nr)
+               goto done;
+
+       while (!list_empty(&refchk->fragments)) {
+               /* Discard any fragments ending at rbno from the worklist. */
+               nr = 0;
+               next_rbno = NULLRGBLOCK;
+               list_for_each_entry_safe(frag, n, &worklist, list) {
+                       bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+                       if (bno != rbno) {
+                               if (bno < next_rbno)
+                                       next_rbno = bno;
+                               continue;
+                       }
+                       list_del(&frag->list);
+                       kfree(frag);
+                       nr++;
+               }
+
+               /* Try to add nr rmaps starting at rbno to the worklist. */
+               list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+                       bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+                       if (frag->rm.rm_startblock != rbno)
+                               goto done;
+                       list_move_tail(&frag->list, &worklist);
+                       if (next_rbno > bno)
+                               next_rbno = bno;
+                       nr--;
+                       if (nr == 0)
+                               break;
+               }
+
+               /*
+                * If we get here and nr > 0, this means that we added fewer
+                * items to the worklist than we discarded because the fragment
+                * list ran out of items.  Therefore, we cannot maintain the
+                * required refcount.  Something is wrong, so we're done.
+                */
+               if (nr)
+                       goto done;
+
+               rbno = next_rbno;
+       }
+
+       /*
+        * Make sure the last extent we processed ends at or beyond
+        * the end of the refcount extent.
+        */
+       if (rbno < refchk->bno + refchk->len)
+               goto done;
+
+       /* Actually record us having seen the remaining refcount. */
+       refchk->seen = refchk->refcount;
+done:
+       /* Delete fragments and work list. */
+       list_for_each_entry_safe(frag, n, &worklist, list) {
+               list_del(&frag->list);
+               kfree(frag);
+       }
+       list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+               list_del(&frag->list);
+               kfree(frag);
+       }
+}
+
+/* Use the rmap entries covering this extent to verify the refcount. */
+STATIC void
+xchk_rtrefcountbt_xref_rmap(
+       struct xfs_scrub                *sc,
+       const struct xfs_refcount_irec  *irec)
+{
+       struct xchk_rtrefcnt_check      refchk = {
+               .sc                     = sc,
+               .bno                    = irec->rc_startblock,
+               .len                    = irec->rc_blockcount,
+               .refcount               = irec->rc_refcount,
+               .seen                   = 0,
+       };
+       struct xfs_rmap_irec            low;
+       struct xfs_rmap_irec            high;
+       struct xchk_rtrefcnt_frag       *frag;
+       struct xchk_rtrefcnt_frag       *n;
+       int                             error;
+
+       if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       /* Cross-reference with the rmapbt to confirm the refcount. */
+       memset(&low, 0, sizeof(low));
+       low.rm_startblock = irec->rc_startblock;
+       memset(&high, 0xFF, sizeof(high));
+       high.rm_startblock = irec->rc_startblock + irec->rc_blockcount - 1;
+
+       INIT_LIST_HEAD(&refchk.fragments);
+       error = xfs_rmap_query_range(sc->sr.rmap_cur, &low, &high,
+                       xchk_rtrefcountbt_rmap_check, &refchk);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur))
+               goto out_free;
+
+       xchk_rtrefcountbt_process_rmap_fragments(&refchk);
+       if (irec->rc_refcount != refchk.seen)
+               xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
+
+out_free:
+       list_for_each_entry_safe(frag, n, &refchk.fragments, list) {
+               list_del(&frag->list);
+               kfree(frag);
+       }
+}
+
+/* Cross-reference with the other btrees. */
+STATIC void
+xchk_rtrefcountbt_xref(
+       struct xfs_scrub                *sc,
+       const struct xfs_refcount_irec  *irec)
+{
+       if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+               return;
+
+       xchk_xref_is_used_rt_space(sc,
+                       xfs_rgbno_to_rtb(sc->sr.rtg, irec->rc_startblock),
+                       irec->rc_blockcount);
+       xchk_rtrefcountbt_xref_rmap(sc, irec);
+}
+
+struct xchk_rtrefcbt_records {
+       /* Previous refcount record. */
+       struct xfs_refcount_irec        prev_rec;
+
+       /* Number of CoW blocks we expect. */
+       xfs_extlen_t                    cow_blocks;
+};
+
+static inline bool
+xchk_rtrefcount_mergeable(
+       struct xchk_rtrefcbt_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_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 >
+                       XFS_REFC_LEN_MAX)
+               return false;
+
+       return true;
+}
+
+/* Flag failures for records that could be merged. */
+STATIC void
+xchk_rtrefcountbt_check_mergeable(
+       struct xchk_btree               *bs,
+       struct xchk_rtrefcbt_records    *rrc,
+       const struct xfs_refcount_irec  *irec)
+{
+       if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+               return;
+
+       if (xchk_rtrefcount_mergeable(rrc, irec))
+               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+
+       memcpy(&rrc->prev_rec, irec, sizeof(struct xfs_refcount_irec));
+}
+
+/* Scrub a rtrefcountbt record. */
+STATIC int
+xchk_rtrefcountbt_rec(
+       struct xchk_btree               *bs,
+       const union xfs_btree_rec       *rec)
+{
+       struct xfs_mount                *mp = bs->cur->bc_mp;
+       struct xchk_rtrefcbt_records    *rrc = bs->private;
+       struct xfs_refcount_irec        irec;
+       u32                             mod;
+
+       xfs_refcount_btrec_to_irec(rec, &irec);
+       if (xfs_rtrefcount_check_irec(to_rtg(bs->cur->bc_group), &irec) !=
+                       NULL) {
+               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+               return 0;
+       }
+
+       /* We can only share full rt extents. */
+       mod = xfs_rgbno_to_rtxoff(mp, irec.rc_startblock);
+       if (mod)
+               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+       mod = xfs_extlen_to_rtxmod(mp, irec.rc_blockcount);
+       if (mod)
+               xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
+
+       if (irec.rc_domain == XFS_REFC_DOMAIN_COW)
+               rrc->cow_blocks += irec.rc_blockcount;
+
+       xchk_rtrefcountbt_check_mergeable(bs, rrc, &irec);
+       xchk_rtrefcountbt_xref(bs->sc, &irec);
+
+       return 0;
+}
+
+/* Make sure we have as many refc blocks as the rmap says. */
+STATIC void
+xchk_refcount_xref_rmap(
+       struct xfs_scrub        *sc,
+       const struct xfs_owner_info *btree_oinfo,
+       xfs_extlen_t            cow_blocks)
+{
+       xfs_extlen_t            refcbt_blocks = 0;
+       xfs_filblks_t           blocks;
+       int                     error;
+
+       if (!sc->sr.rmap_cur || !sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       /* Check that we saw as many refcbt blocks as the rmap knows about. */
+       error = xfs_btree_count_blocks(sc->sr.refc_cur, &refcbt_blocks);
+       if (!xchk_btree_process_error(sc, sc->sr.refc_cur, 0, &error))
+               return;
+       error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo,
+                       &blocks);
+       if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+               return;
+       if (blocks != refcbt_blocks)
+               xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+
+       /* Check that we saw as many cow blocks as the rmap knows about. */
+       error = xchk_count_rmap_ownedby_ag(sc, sc->sr.rmap_cur,
+                       &XFS_RMAP_OINFO_COW, &blocks);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur))
+               return;
+       if (blocks != cow_blocks)
+               xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
+}
+
+/* Scrub the refcount btree for some AG. */
+int
+xchk_rtrefcountbt(
+       struct xfs_scrub        *sc)
+{
+       struct xfs_owner_info   btree_oinfo;
+       struct xchk_rtrefcbt_records rrc = {
+               .cow_blocks     = 0,
+       };
+       int                     error;
+
+       error = xchk_metadata_inode_forks(sc);
+       if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+               return error;
+
+       xfs_rmap_ino_bmbt_owner(&btree_oinfo,
+                       sc->sr.rtg->rtg_inodes[XFS_RTGI_REFCOUNT]->i_ino,
+                       XFS_DATA_FORK);
+       error = xchk_btree(sc, sc->sr.refc_cur, xchk_rtrefcountbt_rec,
+                       &btree_oinfo, &rrc);
+       if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+               return error;
+
+       xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
+
+       return 0;
+}
index 16da054b2eb0dce810720fbc901b1b1200c78f83..6e31f12cef4cc954574a6c07f734c089c8a93747 100644 (file)
@@ -467,6 +467,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
                .has    = xfs_has_rtrmapbt,
                .repair = xrep_rtrmapbt,
        },
+       [XFS_SCRUB_TYPE_RTREFCBT] = {   /* realtime refcountbt */
+               .type   = ST_RTGROUP,
+               .setup  = xchk_setup_rtrefcountbt,
+               .scrub  = xchk_rtrefcountbt,
+               .has    = xfs_has_rtreflink,
+               .repair = xrep_notsupported,
+       },
 };
 
 static int
index 582faa927be719a725daa29f600d67232077cd0c..fd1df43ad1e7f876452a779e43cdc16ccf706a45 100644 (file)
@@ -129,6 +129,7 @@ struct xchk_rt {
 
        /* rtgroup btrees */
        struct xfs_btree_cur    *rmap_cur;
+       struct xfs_btree_cur    *refc_cur;
 };
 
 struct xfs_scrub {
@@ -278,11 +279,13 @@ int xchk_rtbitmap(struct xfs_scrub *sc);
 int xchk_rtsummary(struct xfs_scrub *sc);
 int xchk_rgsuperblock(struct xfs_scrub *sc);
 int xchk_rtrmapbt(struct xfs_scrub *sc);
+int xchk_rtrefcountbt(struct xfs_scrub *sc);
 #else
 # define xchk_rtbitmap         xchk_nothing
 # define xchk_rtsummary                xchk_nothing
 # define xchk_rgsuperblock     xchk_nothing
 # define xchk_rtrmapbt         xchk_nothing
+# define xchk_rtrefcountbt     xchk_nothing
 #endif
 #ifdef CONFIG_XFS_QUOTA
 int xchk_quota(struct xfs_scrub *sc);
index eb6bb170c902b3fb5c860e2902eab641a55dd975..f8a37ea9779192241d75f0aa82ea15fd8cd8824a 100644 (file)
@@ -83,6 +83,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
        [XFS_SCRUB_TYPE_METAPATH]       = "metapath",
        [XFS_SCRUB_TYPE_RGSUPER]        = "rgsuper",
        [XFS_SCRUB_TYPE_RTRMAPBT]       = "rtrmapbt",
+       [XFS_SCRUB_TYPE_RTREFCBT]       = "rtrefcountbt",
 };
 
 /* Format the scrub stats into a text buffer, similar to pcp style. */
index fb86b746bc174a160c7ab7b2ec1a3a455a9bb12e..289e39d1f418ba701d84c6373a65f14fb7febd63 100644 (file)
@@ -77,6 +77,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_METAPATH);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_RGSUPER);
 TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_RTRMAPBT);
+TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_RTREFCBT);
 
 #define XFS_SCRUB_TYPE_STRINGS \
        { XFS_SCRUB_TYPE_PROBE,         "probe" }, \
@@ -111,7 +112,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_RTRMAPBT);
        { XFS_SCRUB_TYPE_BARRIER,       "barrier" }, \
        { XFS_SCRUB_TYPE_METAPATH,      "metapath" }, \
        { XFS_SCRUB_TYPE_RGSUPER,       "rgsuper" }, \
-       { XFS_SCRUB_TYPE_RTRMAPBT,      "rtrmapbt" }
+       { XFS_SCRUB_TYPE_RTRMAPBT,      "rtrmapbt" }, \
+       { XFS_SCRUB_TYPE_RTREFCBT,      "rtrefcountbt" }
 
 #define XFS_SCRUB_FLAG_STRINGS \
        { XFS_SCRUB_IFLAG_REPAIR,               "repair" }, \