[PATCH 33/51] xfs_repair: check existing rmapbt entries against observed rmaps

Darrick J. Wong
2015-10-07T05:08:54Z

If we're running in -n mode, check the rmaps that we observe against
what's in the rmap btree and complain if there's a mismatch.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
include/libxfs.h | 1
repair/phase4.c | 6 ++
repair/rmap.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/rmap.h | 5 ++
repair/scan.c | 19 ++++--
5 files changed, 198 insertions(+), 6 deletions(-)

diff --git a/include/libxfs.h b/include/libxfs.h
index 662dc30..9c85a49 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -77,6 +77,7 @@ extern uint32_t crc32c_le(uint32_t crc, unsigned char const *p, size_t len);
#include "xfs_bmap.h"
#include "xfs_trace.h"
#include "xfs_trans.h"
+#include "xfs_rmap_btree.h"

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/repair/phase4.c b/repair/phase4.c
index cbdb92e..98aab35 100644
--- a/repair/phase4.c
+++ b/repair/phase4.c
@@ -174,6 +174,12 @@ _("unable to add AG %u metadata reverse-mapping data.\n"), agno);
if (error)
do_error(
_("unable to merge AG %u metadata reverse-mapping data.\n"), agno);
+
+ error = check_rmaps(wq->mp, agno);
+ if (error)
+ do_error(
+_("%s while checking reverse-mappings"),
+ strerror(-error));
}

static void
diff --git a/repair/rmap.c b/repair/rmap.c
index a5ea685..7b65d52 100644
--- a/repair/rmap.c
+++ b/repair/rmap.c
@@ -41,6 +41,7 @@ struct xfs_ag_rmap {
};

static struct xfs_ag_rmap *ag_rmaps;
+static bool rmapbt_suspect;

/*
* Compare rmap observations for array sorting.
@@ -413,3 +414,175 @@ dump_rmap(
#else
# define dump_rmap(m, a, r)
#endif
+
+/**
+ * rmap_record_count() -- Return the number of rmap objects for an AG.
+ *
+ * @mp: XFS mount object
+ * @agno: AG number
+ */
+size_t
+rmap_record_count(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno)
+{
+ return slab_count(ag_rmaps[agno].ar_rmaps);
+}
+
+/**
+ * init_rmap_cursor() -- Return a slab cursor that will return rmap
+ * objects in order.
+ * @agno: AG number.
+ * @cur: The new cursor.
+ */
+int
+init_rmap_cursor(
+ xfs_agnumber_t agno,
+ struct xfs_slab_cursor **cur)
+{
+ return init_slab_cursor(ag_rmaps[agno].ar_rmaps, rmap_compare, cur);
+}
+
+/**
+ * rmap_avoid_check() -- Disable the refcount btree check.
+ */
+void
+rmap_avoid_check(void)
+{
+ rmapbt_suspect = true;
+}
+
+/**
+ * check_rmaps() -- Compare the observed reverse mappings against
+ * what's in the ag btree.
+ * @mp: XFS mount object
+ * @agno: AG number
+ */
+int
+check_rmaps(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno)
+{
+ struct xfs_slab_cursor *rm_cur;
+ struct xfs_btree_cur *bt_cur = NULL;
+ int error;
+ int have;
+ int i;
+ struct xfs_buf *agbp = NULL;
+ struct xfs_rmap_irec *rm_rec;
+ struct xfs_rmap_irec tmp;
+ struct xfs_perag *pag; /* per allocation group data */
+
+ if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+ return 0;
+ if (rmapbt_suspect) {
+ if (no_modify && agno == 0)
+ do_warn(_("would rebuild corrupt rmap btrees.\n"));
+ return 0;
+ }
+
+ /* Create cursors to refcount structures */
+ error = init_rmap_cursor(agno, &rm_cur);
+ if (error)
+ return error;
+
+ error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+ if (error)
+ goto err;
+
+ /* Leave the per-ag data "uninitialized" since we rewrite it later */
+ pag = xfs_perag_get(mp, agno);
+ pag->pagf_init = 0;
+ xfs_perag_put(pag);
+
+ bt_cur = xfs_rmapbt_init_cursor(mp, NULL, agbp, agno);
+ if (!bt_cur) {
+ error = -ENOMEM;
+ goto err;
+ }
+
+ rm_rec = pop_slab_cursor(rm_cur);
+ while (rm_rec) {
+ /* Look for a rmap record in the btree */
+ error = xfs_rmap_lookup_eq(bt_cur, rm_rec->rm_startblock,
+ rm_rec->rm_blockcount, rm_rec->rm_owner,
+ rm_rec->rm_offset, &have);
+ if (error)
+ goto err;
+ if (!have) {
+ do_warn(
+_("Missing reverse-mapping record for (%u/%u) %slen %u owner %"PRIx64" \
+%s%soff %"PRIx64"\n"),
+ agno, rm_rec->rm_startblock,
+ XFS_RMAP_IS_UNWRITTEN(rm_rec->rm_blockcount) ?
+ _("unwritten ") : "",
+ XFS_RMAP_LEN(rm_rec->rm_blockcount),
+ rm_rec->rm_owner,
+ XFS_RMAP_IS_ATTR_FORK(rm_rec->rm_offset) ?
+ _("attr ") : "",
+ XFS_RMAP_IS_BMBT(rm_rec->rm_offset) ?
+ _("bmbt ") : "",
+ XFS_RMAP_OFF(rm_rec->rm_offset));
+ goto next_loop;
+ }
+
+ error = xfs_rmap_get_rec(bt_cur, &tmp, &i);
+ if (error)
+ goto err;
+ if (!i) {
+ do_warn(
+_("Unretrievable reverse-mapping record for (%u/%u) %slen %u owner %"PRIx64" \
+%s%soff %"PRIx64"\n"),
+ agno, rm_rec->rm_startblock,
+ XFS_RMAP_IS_UNWRITTEN(rm_rec->rm_blockcount) ?
+ _("unwritten ") : "",
+ XFS_RMAP_LEN(rm_rec->rm_blockcount),
+ rm_rec->rm_owner,
+ XFS_RMAP_IS_ATTR_FORK(rm_rec->rm_offset) ?
+ _("attr ") : "",
+ XFS_RMAP_IS_BMBT(rm_rec->rm_offset) ?
+ _("bmbt ") : "",
+ XFS_RMAP_OFF(rm_rec->rm_offset));
+ goto next_loop;
+ }
+
+ /* Compare each refcount observation against the btree's */
+ if (tmp.rm_startblock != rm_rec->rm_startblock ||
+ tmp.rm_blockcount != rm_rec->rm_blockcount ||
+ tmp.rm_owner != rm_rec->rm_owner ||
+ tmp.rm_offset != rm_rec->rm_offset)
+ do_warn(
+_("Incorrect reverse-mapping: saw (%u/%u) %slen %u owner %"PRIx64" %s%soff \
+%"PRIx64"; should be (%u/%u) %slen %u owner %"PRIx64" %s%soff %"PRIx64"\n"),
+ agno, tmp.rm_startblock,
+ XFS_RMAP_IS_UNWRITTEN(tmp.rm_blockcount) ?
+ _("unwritten ") : "",
+ XFS_RMAP_LEN(tmp.rm_blockcount),
+ tmp.rm_owner,
+ XFS_RMAP_IS_ATTR_FORK(tmp.rm_offset) ?
+ _("attr ") : "",
+ XFS_RMAP_IS_BMBT(tmp.rm_offset) ?
+ _("bmbt ") : "",
+ XFS_RMAP_OFF(tmp.rm_offset),
+ agno, rm_rec->rm_startblock,
+ XFS_RMAP_IS_UNWRITTEN(rm_rec->rm_blockcount) ?
+ _("unwritten ") : "",
+ XFS_RMAP_LEN(rm_rec->rm_blockcount),
+ rm_rec->rm_owner,
+ XFS_RMAP_IS_ATTR_FORK(rm_rec->rm_offset) ?
+ _("attr ") : "",
+ XFS_RMAP_IS_BMBT(rm_rec->rm_offset) ?
+ _("bmbt ") : "",
+ XFS_RMAP_OFF(rm_rec->rm_offset));
+next_loop:
+ rm_rec = pop_slab_cursor(rm_cur);
+ }
+
+err:
+ if (bt_cur)
+ xfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+ if (agbp)
+ libxfs_putbuf(agbp);
+ free_slab_cursor(&rm_cur);
+ return 0;
+}
diff --git a/repair/rmap.h b/repair/rmap.h
index 7bab450..f3f3331 100644
--- a/repair/rmap.h
+++ b/repair/rmap.h
@@ -33,4 +33,9 @@ extern int fold_raw_rmaps(struct xfs_mount *mp, xfs_agnumber_t agno);

extern int add_fixed_ag_rmap_data(struct xfs_mount *, xfs_agnumber_t);

+extern size_t rmap_record_count(struct xfs_mount *, xfs_agnumber_t);
+extern int init_rmap_cursor(xfs_agnumber_t, struct xfs_slab_cursor **);
+extern void rmap_avoid_check(void);
+extern int check_rmaps(struct xfs_mount *, xfs_agnumber_t);
+
#endif /* RMAP_H_ */
diff --git a/repair/scan.c b/repair/scan.c
index db9e131..823401b 100644
--- a/repair/scan.c
+++ b/repair/scan.c
@@ -29,6 +29,7 @@
#include "bmap.h"
#include "progress.h"
#include "threads.h"
+#include "slab.h"
#include "rmap.h"

static xfs_mount_t *mp = NULL;
@@ -808,7 +809,9 @@ scan_rmapbt(

if (magic != XFS_RMAP_CRC_MAGIC) {
name = "(unknown)";
- assert(0);
+ hdr_errors++;
+ suspect++;
+ goto out;
}

if (be32_to_cpu(block->bb_magic) != magic) {
@@ -816,7 +819,7 @@ scan_rmapbt(
be32_to_cpu(block->bb_magic), name, agno, bno);
hdr_errors++;
if (suspect)
- return;
+ goto out;
}

/*
@@ -834,7 +837,7 @@ scan_rmapbt(
level, be16_to_cpu(block->bb_level), name, agno, bno);
hdr_errors++;
if (suspect)
- return;
+ goto out;
}

/* check for btree blocks multiply claimed */
@@ -844,7 +847,7 @@ scan_rmapbt(
do_warn(
_("%s rmap btree block claimed (state %d), agno %d, bno %d, suspect %d\n"),
name, state, agno, bno, suspect);
- return;
+ goto out;
}
set_bmap(agno, bno, XR_E_FS_MAP);

@@ -992,7 +995,7 @@ _("unknown block (%d,%d-%d) mismatch on %s tree, state - %d,%" PRIx64 "\n"),
}
}
}
- return;
+ goto out;
}

/*
@@ -1020,7 +1023,7 @@ _("unknown block (%d,%d-%d) mismatch on %s tree, state - %d,%" PRIx64 "\n"),
mp->m_rmap_mnr[1], mp->m_rmap_mxr[1],
name, agno, bno);
if (suspect)
- return;
+ goto out;
suspect++;
} else if (suspect) {
suspect = 0;
@@ -1043,6 +1046,10 @@ _("unknown block (%d,%d-%d) mismatch on %s tree, state - %d,%" PRIx64 "\n"),
magic, priv, &xfs_rmapbt_buf_ops);
}
}
+
+out:
+ if (suspect)
+ rmap_avoid_check();
}

/*