]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: cross-reference checks with the rt refcount btree
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:13:22 +0000 (21:13 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 24 Jul 2024 05:33:56 +0000 (22:33 -0700)
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 d906fab8443854e74a03ae5fd8dc42c0c49a049d..55fd27c319e012d0416387c29cdc2c1ac2181ab6 100644 (file)
@@ -348,12 +348,30 @@ xchk_bmap_rt_iextent_xref(
 
        xchk_xref_is_used_rt_space(info->sc, irec->br_startblock,
                        irec->br_blockcount);
-       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_free:
        xchk_rtgroup_btcur_free(&info->sc->sr);
index 689bc4032a2d1bcf9a23902c6417ad617da9b031..d0bcca2054fb4296586be92dbdcd3aae917bb4b6 100644 (file)
@@ -155,6 +155,8 @@ xchk_rgbitmap_xref(
 
        rgbno = xfs_rtb_to_rgbno(sc->mp, startblock, &rgno);
        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 (rgb->next_free_rgbno < rgbno)
                xchk_xref_has_rt_owner(sc, rgb->next_free_rgbno,
index e71fe6d93ad1ba4568b5c4260bb052f8bfc72ae7..ed9d5bf41b45a7138be5d346b292646c1f5b6f47 100644 (file)
@@ -492,3 +492,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 cb9ede20f271668164a2357b00622d063f3ab0da..ac788e127b8ff3c5ae31843787ef45ad9e59c683 100644 (file)
@@ -22,6 +22,7 @@
 #include "xfs_rtalloc.h"
 #include "xfs_rtgroup.h"
 #include "xfs_imeta.h"
+#include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -154,6 +155,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(
@@ -169,6 +201,11 @@ xchk_rtrmapbt_xref(
                        irec->rm_startblock);
 
        xchk_xref_is_used_rt_space(sc, rtbno, 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 4e80079c19f4ad0eb72232f6d7e5af50d74c74fa..c64775912db8c0a4d6ee0ef6147a4b122e628d81 100644 (file)
@@ -330,11 +330,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__ */