]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: cross-reference checks with the rt refcount btree
authorDarrick J. Wong <djwong@kernel.org>
Sat, 21 Sep 2024 05:44:11 +0000 (07:44 +0200)
committerChristoph Hellwig <hch@lst.de>
Sun, 22 Sep 2024 08:48:29 +0000 (10:48 +0200)
Use the realtime refcount btree to implement cross-reference checks in
other data structures.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/scrub/bmap.c
fs/xfs/scrub/rtbitmap.c
fs/xfs/scrub/rtrefcount.c
fs/xfs/scrub/rtrmap.c
fs/xfs/scrub/scrub.h

index 705332ff321b190395eb02a6303b921d36b0b4a7..86085c98ef1e8d79c9cc8ff8120c274fb7886fb6 100644 (file)
@@ -347,13 +347,31 @@ xchk_bmap_rt_iextent_xref(
                goto out_cur;
 
        rgbno = xfs_rtb_to_rgbno(info->sc->mp, irec->br_startblock);
-       xchk_bmap_xref_rmap(info, irec, rgbno);
-
-       xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino, info->whichfork,
-                       irec->br_startoff);
-       xchk_xref_is_only_rt_owned_by(info->sc, rgbno,
-                       irec->br_blockcount, &oinfo);
 
+       switch (info->whichfork) {
+       case XFS_DATA_FORK:
+               xchk_bmap_xref_rmap(info, irec, rgbno);
+               if (!xfs_is_reflink_inode(info->sc->ip)) {
+                       xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino,
+                                       info->whichfork, irec->br_startoff);
+                       xchk_xref_is_only_rt_owned_by(info->sc, rgbno,
+                                       irec->br_blockcount, &oinfo);
+                       xchk_xref_is_not_rt_shared(info->sc, rgbno,
+                                       irec->br_blockcount);
+               }
+               xchk_xref_is_not_rt_cow_staging(info->sc, rgbno,
+                               irec->br_blockcount);
+               break;
+       case XFS_COW_FORK:
+               xchk_bmap_xref_rmap_cow(info, irec, rgbno);
+               xchk_xref_is_only_rt_owned_by(info->sc, rgbno,
+                               irec->br_blockcount, &XFS_RMAP_OINFO_COW);
+               xchk_xref_is_rt_cow_staging(info->sc, rgbno,
+                               irec->br_blockcount);
+               xchk_xref_is_not_rt_shared(info->sc, rgbno,
+                               irec->br_blockcount);
+               break;
+       }
 out_cur:
        xchk_rtgroup_btcur_free(&info->sc->sr);
 out_free:
index 557f24be3d0c5c76f6479f37b2e16f12f9a35cfc..0f0a5485e9da0176e29bba5fe35489a359676486 100644 (file)
@@ -106,6 +106,8 @@ xchk_rtbitmap_xref(
                return;
 
        xchk_xref_has_no_rt_owner(sc, rgbno, blockcount);
+       xchk_xref_is_not_rt_shared(sc, rgbno, blockcount);
+       xchk_xref_is_not_rt_cow_staging(sc, rgbno, blockcount);
 
        if (rtb->next_free_rgbno < rgbno)
                xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno,
index 212d86fb9b35e309fff64113be8ebce6f696210e..bdcc96831d9699a3fde959218e38dd542c034fc0 100644 (file)
@@ -487,3 +487,89 @@ xchk_rtrefcountbt(
 
        return 0;
 }
+
+/* xref check that a cow staging extent is marked in the rtrefcountbt. */
+void
+xchk_xref_is_rt_cow_staging(
+       struct xfs_scrub                *sc,
+       xfs_rgblock_t                   bno,
+       xfs_extlen_t                    len)
+{
+       struct xfs_refcount_irec        rc;
+       int                             has_refcount;
+       int                             error;
+
+       if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       /* Find the CoW staging extent. */
+       error = xfs_refcount_lookup_le(sc->sr.refc_cur, XFS_REFC_DOMAIN_COW,
+                       bno, &has_refcount);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+               return;
+       if (!has_refcount) {
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+               return;
+       }
+
+       error = xfs_refcount_get_rec(sc->sr.refc_cur, &rc, &has_refcount);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+               return;
+       if (!has_refcount) {
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+               return;
+       }
+
+       /* CoW lookup returned a shared extent record? */
+       if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
+               xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+
+       /* Must be at least as long as what was passed in */
+       if (rc.rc_blockcount < len)
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+}
+
+/*
+ * xref check that the extent is not shared.  Only file data blocks
+ * can have multiple owners.
+ */
+void
+xchk_xref_is_not_rt_shared(
+       struct xfs_scrub        *sc,
+       xfs_rgblock_t           bno,
+       xfs_extlen_t            len)
+{
+       enum xbtree_recpacking  outcome;
+       int                     error;
+
+       if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       error = xfs_refcount_has_records(sc->sr.refc_cur,
+                       XFS_REFC_DOMAIN_SHARED, bno, len, &outcome);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+               return;
+       if (outcome != XBTREE_RECPACKING_EMPTY)
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+}
+
+/* xref check that the extent is not being used for CoW staging. */
+void
+xchk_xref_is_not_rt_cow_staging(
+       struct xfs_scrub        *sc,
+       xfs_rgblock_t           bno,
+       xfs_extlen_t            len)
+{
+       enum xbtree_recpacking  outcome;
+       int                     error;
+
+       if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       error = xfs_refcount_has_records(sc->sr.refc_cur, XFS_REFC_DOMAIN_COW,
+                       bno, len, &outcome);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+               return;
+       if (outcome != XBTREE_RECPACKING_EMPTY)
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+}
index 0e2dec1071acd8107e396a68d3149d5e801a0481..6add6378289d9bbd1dbbd8868b1fca73af6d8e41 100644 (file)
@@ -22,6 +22,7 @@
 #include "xfs_rtalloc.h"
 #include "xfs_rtgroup.h"
 #include "xfs_metafile.h"
+#include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -150,6 +151,37 @@ xchk_rtrmapbt_check_mergeable(
        memcpy(&cr->prev_rec, irec, sizeof(struct xfs_rmap_irec));
 }
 
+/* Cross-reference a rmap against the refcount btree. */
+STATIC void
+xchk_rtrmapbt_xref_rtrefc(
+       struct xfs_scrub        *sc,
+       struct xfs_rmap_irec    *irec)
+{
+       xfs_rgblock_t           fbno;
+       xfs_extlen_t            flen;
+       bool                    is_inode;
+       bool                    is_bmbt;
+       bool                    is_attr;
+       bool                    is_unwritten;
+       int                     error;
+
+       if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+               return;
+
+       is_inode = !XFS_RMAP_NON_INODE_OWNER(irec->rm_owner);
+       is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK;
+       is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK;
+       is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN;
+
+       /* If this is shared, must be a data fork extent. */
+       error = xfs_refcount_find_shared(sc->sr.refc_cur, irec->rm_startblock,
+                       irec->rm_blockcount, &fbno, &flen, false);
+       if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+               return;
+       if (flen != 0 && (!is_inode || is_attr || is_bmbt || is_unwritten))
+               xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+}
+
 /* Cross-reference with other metadata. */
 STATIC void
 xchk_rtrmapbt_xref(
@@ -162,6 +194,11 @@ xchk_rtrmapbt_xref(
        xchk_xref_is_used_rt_space(sc,
                        xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock),
                        irec->rm_blockcount);
+       if (irec->rm_owner == XFS_RMAP_OWN_COW)
+               xchk_xref_is_cow_staging(sc, irec->rm_startblock,
+                               irec->rm_blockcount);
+       else
+               xchk_rtrmapbt_xref_rtrefc(sc, irec);
 }
 
 /* Scrub a realtime rmapbt record. */
index fd1df43ad1e7f876452a779e43cdc16ccf706a45..0f132ff098e6f731a5f897002b5f3131dd441a4d 100644 (file)
@@ -325,11 +325,20 @@ void xchk_xref_has_rt_owner(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
                xfs_extlen_t len);
 void xchk_xref_is_only_rt_owned_by(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
                xfs_extlen_t len, const struct xfs_owner_info *oinfo);
+void xchk_xref_is_rt_cow_staging(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
+               xfs_extlen_t len);
+void xchk_xref_is_not_rt_shared(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
+               xfs_extlen_t len);
+void xchk_xref_is_not_rt_cow_staging(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
+               xfs_extlen_t len);
 #else
 # define xchk_xref_is_used_rt_space(sc, rtbno, len) do { } while (0)
 # define xchk_xref_has_no_rt_owner(sc, rtbno, len) do { } while (0)
 # define xchk_xref_has_rt_owner(sc, rtbno, len) do { } while (0)
 # define xchk_xref_is_only_rt_owned_by(sc, bno, len, oinfo) do { } while (0)
+# define xchk_xref_is_rt_cow_staging(sc, bno, len) do { } while (0)
+# define xchk_xref_is_not_rt_shared(sc, bno, len) do { } while (0)
+# define xchk_xref_is_not_rt_cow_staging(sc, bno, len) do { } while (0)
 #endif
 
 #endif /* __XFS_SCRUB_SCRUB_H__ */