summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-01-05 17:47:08 -0800
committerDarrick J. Wong <djwong@kernel.org>2021-03-25 17:08:51 -0700
commit52f9e4d68f1b9238c33df80bd1f80832723b42b5 (patch)
treee3ac4e2a8ed4761fbd293125afe96142a1313710
parentd3c926dd003dff968c261db525c69867f68073a5 (diff)
xfs: cross-reference chceks with the rt refcount btree
Use the realtime refcount btree to cross-reference other data structures. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/scrub/bmap.c12
-rw-r--r--fs/xfs/scrub/rtbitmap.c1
-rw-r--r--fs/xfs/scrub/rtrefcount.c66
-rw-r--r--fs/xfs/scrub/rtrmap.c37
-rw-r--r--fs/xfs/scrub/scrub.h6
5 files changed, 122 insertions, 0 deletions
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 448735d9213d..792eb38ecae1 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -341,6 +341,18 @@ 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, irec->br_startblock);
+ switch (info->whichfork) {
+ case XFS_DATA_FORK:
+ if (xfs_is_reflink_inode(info->sc->ip))
+ break;
+ xchk_xref_is_not_shared_rt(info->sc, irec->br_startblock,
+ irec->br_blockcount);
+ break;
+ case XFS_COW_FORK:
+ xchk_xref_is_rt_cow_staging(info->sc, irec->br_startblock,
+ irec->br_blockcount);
+ break;
+ }
xchk_rt_btcur_free(&info->sc->sr);
xchk_rt_unlock(info->sc, &info->sc->sr);
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index ef29b675f0e1..9762d6956d69 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -88,6 +88,7 @@ xchk_rtbitmap_xref(
return;
xchk_xref_has_no_rt_owner(sc, startblock, blockcount);
+ xchk_xref_is_not_shared_rt(sc, startblock, blockcount);
}
/* Scrub a free extent record from the realtime bitmap. */
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
index e5de169cb8e6..b6757cb140d3 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -441,3 +441,69 @@ xchk_rtrefcountbt(
out_unlock:
return error;
}
+
+/* xref check that a cow staging extent is marked in the rtrefcountbt. */
+void
+xchk_xref_is_rt_cow_staging(
+ struct xfs_scrub *sc,
+ xfs_rtblock_t bno,
+ xfs_filblks_t len)
+{
+ struct xfs_refcount_irec rc;
+ bool has_cowflag;
+ 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,
+ bno + XFS_RTREFC_COW_START, &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 flag must be set, refcount must be 1. */
+ has_cowflag = (rc.rc_startblock & XFS_RTREFC_COW_START);
+ if (!has_cowflag || rc.rc_refcount != 1)
+ xchk_btree_xref_set_corrupt(sc, sc->sr.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_shared_rt(
+ struct xfs_scrub *sc,
+ xfs_rtblock_t bno,
+ xfs_filblks_t len)
+{
+ bool shared;
+ int error;
+
+ if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+ return;
+
+ error = xfs_refcount_has_record(sc->sr.refc_cur, bno, len, &shared);
+ if (!xchk_should_check_xref(sc, &error, &sc->sr.refc_cur))
+ return;
+ if (shared)
+ xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+}
diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c
index 346548bb21f1..c079fcade217 100644
--- a/fs/xfs/scrub/rtrmap.c
+++ b/fs/xfs/scrub/rtrmap.c
@@ -20,6 +20,7 @@
#include "xfs_rtrmap_btree.h"
#include "xfs_inode.h"
#include "xfs_rtalloc.h"
+#include "xfs_refcount.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -63,6 +64,37 @@ xchk_setup_rtrmapbt(
/* Realtime reverse mapping. */
+/* Cross-reference a rmap against the refcount btree. */
+STATIC void
+xchk_rtrmapbt_xref_rtrefc(
+ struct xfs_scrub *sc,
+ struct xfs_rmap_irec *irec)
+{
+ xfs_rtblock_t fbno;
+ xfs_filblks_t flen;
+ bool non_inode;
+ bool is_bmbt;
+ bool is_attr;
+ bool is_unwritten;
+ int error;
+
+ if (!sc->sr.refc_cur || xchk_skip_xref(sc->sm))
+ return;
+
+ non_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 && (non_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(
@@ -74,6 +106,11 @@ xchk_rtrmapbt_xref(
xchk_xref_is_used_rt_space(sc, 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. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 2e19fa3ae73a..314d52fcf0fc 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -214,9 +214,15 @@ void xchk_xref_is_used_rt_space(struct xfs_scrub *sc, xfs_rtblock_t rtbno,
xfs_extlen_t len);
void xchk_xref_has_no_rt_owner(struct xfs_scrub *sc, xfs_rtblock_t rtbno,
xfs_filblks_t len);
+void xchk_xref_is_rt_cow_staging(struct xfs_scrub *sc, xfs_rtblock_t bno,
+ xfs_filblks_t len);
+void xchk_xref_is_not_shared_rt(struct xfs_scrub *sc, xfs_rtblock_t bno,
+ xfs_filblks_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_is_rt_cow_staging(sc, bno, len) do { } while (0)
+# define xchk_xref_is_not_shared_rt(sc, bno, len) do { } while (0)
#endif
struct xchk_fscounters {