summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:19:44 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-10-22 16:41:10 -0700
commit61868eecf1989c1eda421d596d17c27317237e7a (patch)
tree0633e90ecd264383c5bf968635a1686690332524 /fs
parent732f6fe3d46895365dc7040d5098ff55929cd6d5 (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>
Diffstat (limited to 'fs')
-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 5cc07e090a09..f64885d3852c 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -327,6 +327,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 77a50d1dafb3..551f53a40aca 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -63,6 +63,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 21b74b9923de..ae0897b081ab 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -447,3 +447,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 ead9a2fd35e0..e76ad05bc0bb 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 95edf28a0e19..4adbda457782 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -224,9 +224,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 {