summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:19:45 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-12-15 17:29:24 -0800
commit68a6080eaaf32d104c0ae4d54c84e8719e4f5613 (patch)
treed90bbb7eedbe4b346b939645c28ff519659da4a8 /fs/xfs/scrub
parenta49093087eaef880d77191f4d2e6e66cbb664146 (diff)
xfs: check reference counts of gaps between rt refcount records
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r--fs/xfs/scrub/rtrefcount.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c
index ae0897b081ab..2a7d5268bd43 100644
--- a/fs/xfs/scrub/rtrefcount.c
+++ b/fs/xfs/scrub/rtrefcount.c
@@ -13,6 +13,7 @@
#include "xfs_rmap.h"
#include "xfs_refcount.h"
#include "xfs_inode.h"
+#include "xfs_rtalloc.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/btree.h"
@@ -181,7 +182,6 @@ xchk_rtrefcountbt_process_rmap_fragments(
*/
INIT_LIST_HEAD(&worklist);
rbno = NULLRTBLOCK;
- nr = 1;
/* Make sure the fragments actually /are/ in bno order. */
bno = 0;
@@ -195,15 +195,14 @@ xchk_rtrefcountbt_process_rmap_fragments(
* 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)
- goto done;
+ 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);
- if (nr == target_nr)
- break;
nr++;
}
@@ -340,8 +339,56 @@ xchk_rtrefcountbt_xref(
struct xchk_rtrefcbt_records {
xfs_filblks_t cow_blocks;
+ xfs_rtblock_t next_rtbno;
};
+STATIC int
+xchk_rtrefcountbt_rmap_check_gap(
+ struct xfs_btree_cur *cur,
+ const struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ xfs_rtblock_t *next_bno = priv;
+
+ if (*next_bno != NULLRTBLOCK && rec->rm_startblock < *next_bno)
+ return -ECANCELED;
+
+ *next_bno = rec->rm_startblock + rec->rm_blockcount;
+ return 0;
+}
+
+/*
+ * Make sure that a gap in the reference count records does not correspond to
+ * overlapping records (i.e. shared extents) in the reverse mappings.
+ */
+static inline void
+xchk_rtrefcountbt_xref_gaps(
+ struct xfs_scrub *sc,
+ struct xchk_rtrefcbt_records *rrc,
+ xfs_rtblock_t bno)
+{
+ struct xfs_rmap_irec low;
+ struct xfs_rmap_irec high;
+ xfs_rtblock_t next_bno = NULLRTBLOCK;
+ int error;
+
+ if (bno <= rrc->next_rtbno || !sc->sr.rmap_cur ||
+ xchk_skip_xref(sc->sm))
+ return;
+
+ memset(&low, 0, sizeof(low));
+ low.rm_startblock = rrc->next_rtbno;
+ memset(&high, 0xFF, sizeof(high));
+ high.rm_startblock = bno - 1;
+
+ error = xfs_rmap_query_range(sc->sr.rmap_cur, &low, &high,
+ xchk_rtrefcountbt_rmap_check_gap, &next_bno);
+ if (error == -ECANCELED)
+ xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
+ else
+ xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur);
+}
+
/* Scrub a rtrefcountbt record. */
STATIC int
xchk_rtrefcountbt_rec(
@@ -387,6 +434,16 @@ xchk_rtrefcountbt_rec(
xchk_rtrefcountbt_xref(bs->sc, bno, len, refcount);
+ /*
+ * If this is a record for a shared extent, check that all blocks
+ * between the previous record and this one have at most one reverse
+ * mapping.
+ */
+ if (!has_cowflag) {
+ xchk_rtrefcountbt_xref_gaps(bs->sc, rrc, bno);
+ rrc->next_rtbno = bno + len;
+ }
+
return 0;
}
@@ -432,6 +489,7 @@ xchk_rtrefcountbt(
struct xfs_owner_info btree_oinfo;
struct xchk_rtrefcbt_records rrc = {
.cow_blocks = 0,
+ .next_rtbno = 0,
};
int error;
@@ -442,6 +500,12 @@ xchk_rtrefcountbt(
if (error)
goto out_unlock;
+ /*
+ * Check that all blocks between the last refcount > 1 record and the
+ * end of the rt volume have at most one reverse mapping.
+ */
+ xchk_rtrefcountbt_xref_gaps(sc, &rrc, sc->mp->m_sb.sb_rblocks);
+
xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
out_unlock: