* [PATCH 01/15] xfs: add scrub cross-referencing helpers for the free space btrees
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:56 ` [PATCH 02/15] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Add a couple of functions to the free space btrees that will be used
to cross-reference metadata against the bnobt/cntbt, and a generic
btree function that provides the real implementation.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc.c | 21 ++++++++++++++++++++-
fs/xfs/libxfs/xfs_alloc.h | 10 ++++++++++
fs/xfs/libxfs/xfs_btree.c | 29 +++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_btree.h | 2 ++
4 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 83ed771..82333fc 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -167,7 +167,7 @@ xfs_alloc_lookup_ge(
* Lookup the first record less than or equal to [bno, len]
* in the btree given by cur.
*/
-static int /* error */
+int /* error */
xfs_alloc_lookup_le(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_agblock_t bno, /* starting block of extent */
@@ -2981,3 +2981,22 @@ xfs_verify_fsbno(
return false;
return xfs_verify_agbno(mp, agno, XFS_FSB_TO_AGBNO(mp, fsbno));
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_alloc_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.a.ar_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.a.ar_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 7ba2d12..65a0caf 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -198,6 +198,13 @@ xfs_free_extent(
enum xfs_ag_resv_type type); /* block reservation type */
int /* error */
+xfs_alloc_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat); /* success/failure */
+
+int /* error */
xfs_alloc_lookup_ge(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_agblock_t bno, /* starting block of extent */
@@ -237,4 +244,7 @@ bool xfs_verify_agbno(struct xfs_mount *mp, xfs_agnumber_t agno,
xfs_agblock_t agbno);
bool xfs_verify_fsbno(struct xfs_mount *mp, xfs_fsblock_t fsbno);
+int xfs_alloc_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+ xfs_extlen_t len, bool *exist);
+
#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 5f33adf..e0bdff3 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -4953,3 +4953,32 @@ xfs_btree_diff_two_ptrs(
return (int64_t)be64_to_cpu(a->l) - be64_to_cpu(b->l);
return (int64_t)be32_to_cpu(a->s) - be32_to_cpu(b->s);
}
+
+/* If there's an extent, we're done. */
+STATIC int
+xfs_btree_has_record_helper(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ void *priv)
+{
+ return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/* Is there a record covering a given range of keys? */
+int
+xfs_btree_has_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_irec *low,
+ union xfs_btree_irec *high,
+ bool *exists)
+{
+ int error;
+
+ error = xfs_btree_query_range(cur, low, high,
+ &xfs_btree_has_record_helper, NULL);
+ if (error && error != XFS_BTREE_QUERY_RANGE_ABORT)
+ return error;
+ *exists = error == XFS_BTREE_QUERY_RANGE_ABORT;
+
+ return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index b57501c..551a2a0 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -545,5 +545,7 @@ void xfs_btree_get_keys(struct xfs_btree_cur *cur,
struct xfs_btree_block *block, union xfs_btree_key *key);
union xfs_btree_key *xfs_btree_high_key_from_key(struct xfs_btree_cur *cur,
union xfs_btree_key *key);
+int xfs_btree_has_record(struct xfs_btree_cur *cur, union xfs_btree_irec *low,
+ union xfs_btree_irec *high, bool *exists);
#endif /* __XFS_BTREE_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 02/15] xfs: add scrub cross-referencing helpers for the inode btrees
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
2017-12-13 23:56 ` [PATCH 01/15] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:56 ` [PATCH 03/15] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Add a couple of functions to the inode btrees that will be used
to cross-reference metadata against the inobt.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_ialloc.c | 99 ++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_ialloc.h | 6 +++
2 files changed, 105 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index de3f04a..72b6b74 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2755,3 +2755,102 @@ xfs_verify_dir_ino(
return false;
return xfs_verify_ino(mp, ino);
}
+
+/* Is there an inode record covering a given range of inode numbers? */
+int
+xfs_ialloc_has_inode_record(
+ struct xfs_btree_cur *cur,
+ xfs_agino_t low,
+ xfs_agino_t high,
+ bool *exists)
+{
+ struct xfs_inobt_rec_incore irec;
+ xfs_agino_t agino;
+ uint16_t holemask;
+ int has;
+ int i;
+ int error;
+
+ *exists = false;
+ error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has);
+ while (error == 0 && has) {
+ error = xfs_inobt_get_rec(cur, &irec, &has);
+ if (error || irec.ir_startino > high)
+ break;
+
+ agino = irec.ir_startino;
+ holemask = irec.ir_holemask;
+ for (i = 0; i < XFS_INOBT_HOLEMASK_BITS; holemask >>= 1,
+ i++, agino += XFS_INODES_PER_HOLEMASK_BIT) {
+ if (holemask & 1)
+ continue;
+ if (agino + XFS_INODES_PER_HOLEMASK_BIT > low &&
+ agino <= high) {
+ *exists = true;
+ goto out;
+ }
+ }
+
+ error = xfs_btree_increment(cur, 0, &has);
+ }
+out:
+ return error;
+}
+
+/* Is there an inode record covering a given extent? */
+int
+xfs_ialloc_has_inodes_at_extent(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ xfs_agino_t low;
+ xfs_agino_t high;
+
+ low = XFS_OFFBNO_TO_AGINO(cur->bc_mp, bno, 0);
+ high = XFS_OFFBNO_TO_AGINO(cur->bc_mp, bno + len, 0) - 1;
+
+ return xfs_ialloc_has_inode_record(cur, low, high, exists);
+}
+
+struct xfs_ialloc_count_inodes {
+ xfs_agino_t count;
+ xfs_agino_t freecount;
+};
+
+/* Record inode counts across all inobt records. */
+STATIC int
+xfs_ialloc_count_inodes_helper(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ void *priv)
+{
+ struct xfs_inobt_rec_incore irec;
+ struct xfs_ialloc_count_inodes *ci = priv;
+
+ xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
+ ci->count += irec.ir_count;
+ ci->freecount += irec.ir_freecount;
+
+ return 0;
+}
+
+/* Count allocated and free inodes under an inobt. */
+int
+xfs_ialloc_count_inodes(
+ struct xfs_btree_cur *cur,
+ xfs_agino_t *count,
+ xfs_agino_t *freecount)
+{
+ struct xfs_ialloc_count_inodes ci = {0};
+ int error;
+
+ ASSERT(cur->bc_btnum == XFS_BTNUM_INO);
+ error = xfs_btree_query_all(cur, xfs_ialloc_count_inodes_helper, &ci);
+ if (!error) {
+ *count = ci.count;
+ *freecount = ci.freecount;
+ }
+ return error;
+}
diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h
index d2bdcd5..bf26efb 100644
--- a/fs/xfs/libxfs/xfs_ialloc.h
+++ b/fs/xfs/libxfs/xfs_ialloc.h
@@ -171,6 +171,12 @@ int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
union xfs_btree_rec;
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec,
struct xfs_inobt_rec_incore *irec);
+int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
+ xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
+int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
+ xfs_agino_t high, bool *exists);
+int xfs_ialloc_count_inodes(struct xfs_btree_cur *cur, xfs_agino_t *count,
+ xfs_agino_t *freecount);
int xfs_ialloc_cluster_alignment(struct xfs_mount *mp);
void xfs_ialloc_agino_range(struct xfs_mount *mp, xfs_agnumber_t agno,
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 03/15] xfs: add scrub cross-referencing helpers for the rmap btrees
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
2017-12-13 23:56 ` [PATCH 01/15] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
2017-12-13 23:56 ` [PATCH 02/15] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:56 ` [PATCH 04/15] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Add a couple of functions to the rmap btrees that will be used
to cross-reference metadata against the rmapbt.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_rmap.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_rmap.h | 5 ++++
2 files changed, 63 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index 50db920..ea78ec3 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -2387,3 +2387,61 @@ xfs_rmap_compare(
else
return 0;
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_rmap_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.r.rm_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.r.rm_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
+
+/* Is there a record covering a given extent? */
+int
+xfs_rmap_record_exists(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ struct xfs_owner_info *oinfo,
+ bool *has_rmap)
+{
+ uint64_t owner;
+ uint64_t offset;
+ unsigned int flags;
+ int stat;
+ struct xfs_rmap_irec irec;
+ int error;
+
+ xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+
+ error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, flags, &stat);
+ if (error)
+ return error;
+ if (!stat) {
+ *has_rmap = false;
+ return 0;
+ }
+
+ error = xfs_rmap_get_rec(cur, &irec, &stat);
+ if (error)
+ return error;
+ if (!stat) {
+ *has_rmap = false;
+ return 0;
+ }
+
+ *has_rmap = (irec.rm_owner == owner && irec.rm_startblock <= bno &&
+ irec.rm_startblock + irec.rm_blockcount >= bno + len);
+ return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_rmap.h b/fs/xfs/libxfs/xfs_rmap.h
index 0fcd5b1..380e53b 100644
--- a/fs/xfs/libxfs/xfs_rmap.h
+++ b/fs/xfs/libxfs/xfs_rmap.h
@@ -233,5 +233,10 @@ int xfs_rmap_compare(const struct xfs_rmap_irec *a,
union xfs_btree_rec;
int xfs_rmap_btrec_to_irec(union xfs_btree_rec *rec,
struct xfs_rmap_irec *irec);
+int xfs_rmap_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+ xfs_extlen_t len, bool *exists);
+int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+ xfs_extlen_t len, struct xfs_owner_info *oinfo,
+ bool *has_rmap);
#endif /* __XFS_RMAP_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 04/15] xfs: add scrub cross-referencing helpers for the refcount btrees
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (2 preceding siblings ...)
2017-12-13 23:56 ` [PATCH 03/15] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:56 ` [PATCH 05/15] xfs: set up scrub cross-referencing helpers Darrick J. Wong
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Add a couple of functions to the refcount btrees that will be used
to cross-reference metadata against the refcountbt.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_refcount.c | 19 +++++++++++++++++++
fs/xfs/libxfs/xfs_refcount.h | 3 +++
2 files changed, 22 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index c40d267..bee68c2 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -1696,3 +1696,22 @@ xfs_refcount_recover_cow_leftovers(
xfs_trans_brelse(tp, agbp);
goto out_trans;
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_refcount_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.rc.rc_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.rc.rc_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index eafb9d1..2a731ac 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -83,4 +83,7 @@ static inline xfs_fileoff_t xfs_refcount_max_unmap(int log_res)
return (log_res * 3 / 4) / XFS_REFCOUNT_ITEM_OVERHEAD;
}
+extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
+ xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
+
#endif /* __XFS_REFCOUNT_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 05/15] xfs: set up scrub cross-referencing helpers
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (3 preceding siblings ...)
2017-12-13 23:56 ` [PATCH 04/15] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:56 ` [PATCH 06/15] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create some helper functions that we'll use later to deal with problems
we might encounter while cross referencing metadata with other metadata.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/scrub/btree.c | 69 ++++++++++++++++++++-----
fs/xfs/scrub/btree.h | 9 +++
fs/xfs/scrub/common.c | 138 +++++++++++++++++++++++++++++++++++++++++++++----
fs/xfs/scrub/common.h | 22 ++++++++
fs/xfs/scrub/scrub.c | 10 ++++
fs/xfs/scrub/trace.h | 22 ++++++++
6 files changed, 248 insertions(+), 22 deletions(-)
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index df07661..8d48b5b1 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -42,12 +42,14 @@
* Check for btree operation errors. See the section about handling
* operational errors in common.c.
*/
-bool
-xfs_scrub_btree_process_error(
+static bool
+__xfs_scrub_btree_process_error(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level,
- int *error)
+ int *error,
+ bool xref,
+ void *ret_ip)
{
if (*error == 0)
return true;
@@ -60,36 +62,78 @@ xfs_scrub_btree_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ sc->sm->sm_flags |= xfs_scrub_corrupt_flag(xref);
*error = 0;
/* fall through */
default:
if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
trace_xfs_scrub_ifork_btree_op_error(sc, cur, level,
- *error, __return_address);
+ *error, ret_ip);
else
trace_xfs_scrub_btree_op_error(sc, cur, level,
- *error, __return_address);
+ *error, ret_ip);
break;
}
return false;
}
+bool
+xfs_scrub_btree_process_error(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ int level,
+ int *error)
+{
+ return __xfs_scrub_btree_process_error(sc, cur, level, error, false,
+ __return_address);
+}
+
+bool
+xfs_scrub_btree_xref_process_error(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ int level,
+ int *error)
+{
+ return __xfs_scrub_btree_process_error(sc, cur, level, error, true,
+ __return_address);
+}
+
/* Record btree block corruption. */
-void
-xfs_scrub_btree_set_corrupt(
+static void
+__xfs_scrub_btree_set_corrupt(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
- int level)
+ int level,
+ bool xref,
+ void *ret_ip)
{
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ sc->sm->sm_flags |= xfs_scrub_corrupt_flag(xref);
if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
trace_xfs_scrub_ifork_btree_error(sc, cur, level,
- __return_address);
+ ret_ip);
else
trace_xfs_scrub_btree_error(sc, cur, level,
- __return_address);
+ ret_ip);
+}
+
+void
+xfs_scrub_btree_set_corrupt(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ __xfs_scrub_btree_set_corrupt(sc, cur, level, false, __return_address);
+}
+
+void
+xfs_scrub_btree_xref_set_corrupt(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ __xfs_scrub_btree_set_corrupt(sc, cur, level, true, __return_address);
}
/*
@@ -512,5 +556,6 @@ xfs_scrub_btree(
}
out:
+
return error;
}
diff --git a/fs/xfs/scrub/btree.h b/fs/xfs/scrub/btree.h
index 4de825a6..e2b868e 100644
--- a/fs/xfs/scrub/btree.h
+++ b/fs/xfs/scrub/btree.h
@@ -26,10 +26,19 @@
bool xfs_scrub_btree_process_error(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level, int *error);
+/* Check for btree xref operation errors. */
+bool xfs_scrub_btree_xref_process_error(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur, int level,
+ int *error);
+
/* Check for btree corruption. */
void xfs_scrub_btree_set_corrupt(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level);
+/* Check for btree xref discrepancies. */
+void xfs_scrub_btree_xref_set_corrupt(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur, int level);
+
struct xfs_scrub_btree;
typedef int (*xfs_scrub_btree_rec_fn)(
struct xfs_scrub_btree *bs,
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 98452ad..effdcfa 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -78,12 +78,14 @@
*/
/* Check for operational errors. */
-bool
-xfs_scrub_process_error(
+static bool
+__xfs_scrub_process_error(
struct xfs_scrub_context *sc,
xfs_agnumber_t agno,
xfs_agblock_t bno,
- int *error)
+ int *error,
+ bool xref,
+ void *ret_ip)
{
switch (*error) {
case 0:
@@ -95,24 +97,48 @@ xfs_scrub_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ sc->sm->sm_flags |= xfs_scrub_corrupt_flag(xref);
*error = 0;
/* fall through */
default:
trace_xfs_scrub_op_error(sc, agno, bno, *error,
- __return_address);
+ ret_ip);
break;
}
return false;
}
-/* Check for operational errors for a file offset. */
bool
-xfs_scrub_fblock_process_error(
+xfs_scrub_process_error(
+ struct xfs_scrub_context *sc,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ int *error)
+{
+ return __xfs_scrub_process_error(sc, agno, bno, error, false,
+ __return_address);
+}
+
+bool
+xfs_scrub_xref_process_error(
+ struct xfs_scrub_context *sc,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ int *error)
+{
+ return __xfs_scrub_process_error(sc, agno, bno, error, true,
+ __return_address);
+}
+
+/* Check for operational errors for a file offset. */
+static bool
+__xfs_scrub_fblock_process_error(
struct xfs_scrub_context *sc,
int whichfork,
xfs_fileoff_t offset,
- int *error)
+ int *error,
+ bool xref,
+ void *ret_ip)
{
switch (*error) {
case 0:
@@ -124,17 +150,39 @@ xfs_scrub_fblock_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+ sc->sm->sm_flags |= xfs_scrub_corrupt_flag(xref);
*error = 0;
/* fall through */
default:
trace_xfs_scrub_file_op_error(sc, whichfork, offset, *error,
- __return_address);
+ ret_ip);
break;
}
return false;
}
+bool
+xfs_scrub_fblock_process_error(
+ struct xfs_scrub_context *sc,
+ int whichfork,
+ xfs_fileoff_t offset,
+ int *error)
+{
+ return __xfs_scrub_fblock_process_error(sc, whichfork, offset, error,
+ false, __return_address);
+}
+
+bool
+xfs_scrub_fblock_xref_process_error(
+ struct xfs_scrub_context *sc,
+ int whichfork,
+ xfs_fileoff_t offset,
+ int *error)
+{
+ return __xfs_scrub_fblock_process_error(sc, whichfork, offset, error,
+ true, __return_address);
+}
+
/*
* Handling scrub corruption/optimization/warning checks.
*
@@ -183,6 +231,16 @@ xfs_scrub_block_set_corrupt(
trace_xfs_scrub_block_error(sc, bp->b_bn, __return_address);
}
+/* Record a corruption while cross-referencing. */
+void
+xfs_scrub_block_xref_set_corrupt(
+ struct xfs_scrub_context *sc,
+ struct xfs_buf *bp)
+{
+ sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+ trace_xfs_scrub_block_error(sc, bp->b_bn, __return_address);
+}
+
/*
* Record a corrupt inode. The trace data will include the block given
* by bp if bp is given; otherwise it will use the block location of the
@@ -198,6 +256,17 @@ xfs_scrub_ino_set_corrupt(
trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
}
+/* Record a corruption while cross-referencing with an inode. */
+void
+xfs_scrub_ino_xref_set_corrupt(
+ struct xfs_scrub_context *sc,
+ xfs_ino_t ino,
+ struct xfs_buf *bp)
+{
+ sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+ trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
+}
+
/* Record corruption in a block indexed by a file fork. */
void
xfs_scrub_fblock_set_corrupt(
@@ -209,6 +278,17 @@ xfs_scrub_fblock_set_corrupt(
trace_xfs_scrub_fblock_error(sc, whichfork, offset, __return_address);
}
+/* Record a corruption while cross-referencing a fork block. */
+void
+xfs_scrub_fblock_xref_set_corrupt(
+ struct xfs_scrub_context *sc,
+ int whichfork,
+ xfs_fileoff_t offset)
+{
+ sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+ trace_xfs_scrub_fblock_error(sc, whichfork, offset, __return_address);
+}
+
/*
* Warn about inodes that need administrative review but is not
* incorrect.
@@ -564,3 +644,41 @@ xfs_scrub_setup_inode_contents(
/* scrub teardown will unlock and release the inode for us */
return error;
}
+
+/*
+ * Predicate that decides if we need to evaluate the cross-reference check.
+ * If there was an error accessing the cross-reference btree, just delete
+ * the cursor and skip the check.
+ */
+bool
+xfs_scrub_should_xref(
+ struct xfs_scrub_context *sc,
+ int *error,
+ struct xfs_btree_cur **curpp)
+{
+ /* If not a btree cross-reference, just check the error code. */
+ if (curpp == NULL) {
+ if (*error == 0)
+ return true;
+ goto fail;
+ }
+
+ ASSERT(*curpp != NULL);
+ /* If no error or we've already given up on xref, just bail out. */
+ if (*error == 0 || *curpp == NULL)
+ return true;
+
+ /* xref error, delete cursor and bail out. */
+ sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+ xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
+ *curpp = NULL;
+fail:
+ trace_xfs_scrub_xref_error(sc, *error, __return_address);
+
+ /*
+ * Errors encountered during cross-referencing with another
+ * data structure should not cause this scrubber to abort.
+ */
+ *error = 0;
+ return false;
+}
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index fe12053..0e12a10 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -56,6 +56,11 @@ bool xfs_scrub_process_error(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset, int *error);
+bool xfs_scrub_xref_process_error(struct xfs_scrub_context *sc,
+ xfs_agnumber_t agno, xfs_agblock_t bno, int *error);
+bool xfs_scrub_fblock_xref_process_error(struct xfs_scrub_context *sc,
+ int whichfork, xfs_fileoff_t offset, int *error);
+
void xfs_scrub_block_set_preen(struct xfs_scrub_context *sc,
struct xfs_buf *bp);
void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino,
@@ -68,6 +73,13 @@ void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
void xfs_scrub_fblock_set_corrupt(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset);
+void xfs_scrub_block_xref_set_corrupt(struct xfs_scrub_context *sc,
+ struct xfs_buf *bp);
+void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
+ struct xfs_buf *bp);
+void xfs_scrub_fblock_xref_set_corrupt(struct xfs_scrub_context *sc,
+ int whichfork, xfs_fileoff_t offset);
+
void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino,
struct xfs_buf *bp);
void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
@@ -76,6 +88,10 @@ void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
void xfs_scrub_set_incomplete(struct xfs_scrub_context *sc);
int xfs_scrub_checkpoint_log(struct xfs_mount *mp);
+/* Are we set up for a cross-referencing operation? */
+bool xfs_scrub_should_xref(struct xfs_scrub_context *sc, int *error,
+ struct xfs_btree_cur **curpp);
+
/* Setup functions */
int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc,
@@ -139,4 +155,10 @@ int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in);
int xfs_scrub_setup_inode_contents(struct xfs_scrub_context *sc,
struct xfs_inode *ip, unsigned int resblks);
+/* Figure out the correct corruption flag for whatever's wrong. */
+static inline __u32 xfs_scrub_corrupt_flag(bool xref)
+{
+ return xref ? XFS_SCRUB_OFLAG_XCORRUPT : XFS_SCRUB_OFLAG_CORRUPT;
+}
+
#endif /* __XFS_SCRUB_COMMON_H__ */
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index b98084d..93cbe5f 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -111,6 +111,16 @@
* structure itself is corrupt, the CORRUPT flag will be set. If
* the metadata is correct but otherwise suboptimal, the PREEN flag
* will be set.
+ *
+ * We perform secondary validation of filesystem metadata by
+ * cross-referencing every record with all other available metadata.
+ * For example, for block mapping extents, we verify that there are no
+ * records in the free space and inode btrees corresponding to that
+ * space extent and that there is a corresponding entry in the reverse
+ * mapping btree. Inconsistent metadata is noted by setting the
+ * XCORRUPT flag; btree query function errors are noted by setting the
+ * XFAIL flag and deleting the cursor to prevent further attempts to
+ * cross-reference with a defective btree.
*/
/*
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index c4ebfb5..81becf6 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -491,6 +491,28 @@ DEFINE_EVENT(xfs_scrub_sbtree_class, name, \
DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_rec);
DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_key);
+TRACE_EVENT(xfs_scrub_xref_error,
+ TP_PROTO(struct xfs_scrub_context *sc, int error, void *ret_ip),
+ TP_ARGS(sc, error, ret_ip),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(int, type)
+ __field(int, error)
+ __field(void *, ret_ip)
+ ),
+ TP_fast_assign(
+ __entry->dev = sc->mp->m_super->s_dev;
+ __entry->type = sc->sm->sm_type;
+ __entry->error = error;
+ __entry->ret_ip = ret_ip;
+ ),
+ TP_printk("dev %d:%d type %u xref error %d ret_ip %pF",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->type,
+ __entry->error,
+ __entry->ret_ip)
+);
+
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
#undef TRACE_INCLUDE_PATH
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 06/15] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (4 preceding siblings ...)
2017-12-13 23:56 ` [PATCH 05/15] xfs: set up scrub cross-referencing helpers Darrick J. Wong
@ 2017-12-13 23:56 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 07/15] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:56 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
When scanning a metadata btree block, cross-reference the block location
with the free space btree and the reverse mapping btree to ensure that
the rmapbt knows about the block and the bnobt does not. Add a
mechanism to defer checks when we happen to be scanning the bnobt/rmapbt
itself because it's less efficient to repeatedly clone and destroy the
cursor.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/scrub/btree.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 8d48b5b1..40fc26f 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -359,6 +359,80 @@ xfs_scrub_btree_block_check_siblings(
return error;
}
+struct check_owner {
+ struct list_head list;
+ xfs_daddr_t daddr;
+ int level;
+};
+
+/*
+ * Make sure this btree block isn't in the free list and that there's
+ * an rmap record for it.
+ */
+STATIC int
+xfs_scrub_btree_check_block_owner(
+ struct xfs_scrub_btree *bs,
+ int level,
+ xfs_daddr_t daddr)
+{
+ struct xfs_scrub_ag sa = { 0 };
+ struct xfs_scrub_ag *psa;
+ xfs_agnumber_t agno;
+ int error = 0;
+
+ agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
+
+ if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ error = xfs_scrub_ag_init(bs->sc, agno, &sa);
+ if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur,
+ level, &error))
+ return error;
+ psa = &sa;
+ } else {
+ psa = &bs->sc->sa;
+ }
+
+ if (psa == &sa)
+ xfs_scrub_ag_free(bs->sc, &sa);
+
+ return error;
+}
+
+/* Check the owner of a btree block. */
+STATIC int
+xfs_scrub_btree_check_owner(
+ struct xfs_scrub_btree *bs,
+ int level,
+ struct xfs_buf *bp)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ struct check_owner *co;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
+ return 0;
+
+ /*
+ * We want to cross-reference each btree block with the bnobt
+ * and the rmapbt. We cannot cross-reference the bnobt or
+ * rmapbt while scanning the bnobt or rmapbt, respectively,
+ * because we cannot alter the cursor and we'd prefer not to
+ * duplicate cursors. Therefore, save the buffer daddr for
+ * later scanning.
+ */
+ if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
+ co = kmem_alloc(sizeof(struct check_owner),
+ KM_MAYFAIL | KM_NOFS);
+ if (!co)
+ return -ENOMEM;
+ co->level = level;
+ co->daddr = XFS_BUF_ADDR(bp);
+ list_add_tail(&co->list, &bs->to_check);
+ return 0;
+ }
+
+ return xfs_scrub_btree_check_block_owner(bs, level, XFS_BUF_ADDR(bp));
+}
+
/*
* Grab and scrub a btree block given a btree pointer. Returns block
* and buffer pointers (if applicable) if they're ok to use.
@@ -395,6 +469,14 @@ xfs_scrub_btree_get_block(
}
/*
+ * Check the block's owner; this function absorbs error codes
+ * for us.
+ */
+ error = xfs_scrub_btree_check_owner(bs, level, *pbp);
+ if (error)
+ return error;
+
+ /*
* Check the block's siblings; this function absorbs error codes
* for us.
*/
@@ -465,6 +547,8 @@ xfs_scrub_btree(
struct xfs_btree_block *block;
int level;
struct xfs_buf *bp;
+ struct check_owner *co;
+ struct check_owner *n;
int i;
int error = 0;
@@ -556,6 +640,14 @@ xfs_scrub_btree(
}
out:
+ /* Process deferred owner checks on btree blocks. */
+ list_for_each_entry_safe(co, n, &bs.to_check, list) {
+ if (!error)
+ error = xfs_scrub_btree_check_block_owner(&bs,
+ co->level, co->daddr);
+ list_del(&co->list);
+ kmem_free(co);
+ }
return error;
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 07/15] xfs: introduce scrubber cross-referencing stubs
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (5 preceding siblings ...)
2017-12-13 23:56 ` [PATCH 06/15] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 08/15] xfs: cross-reference with the bnobt Darrick J. Wong
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create some stubs that will be used to cross-reference metadata records.
The actual cross-referencing will be filled in by subsequent patches.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 65 ++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/scrub/alloc.c | 14 ++++++++++
fs/xfs/scrub/bmap.c | 28 ++++++++++++++++++++
fs/xfs/scrub/ialloc.c | 17 ++++++++++++
fs/xfs/scrub/inode.c | 13 +++++++++
fs/xfs/scrub/refcount.c | 15 +++++++++++
fs/xfs/scrub/rmap.c | 13 +++++++++
7 files changed, 163 insertions(+), 2 deletions(-)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index b599358..b4297e9 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -37,7 +37,10 @@
#include "scrub/common.h"
#include "scrub/trace.h"
-/* Walk all the blocks in the AGFL. */
+/*
+ * Walk all the blocks in the AGFL. The fn function can return any negative
+ * error code or XFS_BTREE_QUERY_RANGE_ABORT.
+ */
int
xfs_scrub_walk_agfl(
struct xfs_scrub_context *sc,
@@ -98,6 +101,14 @@ xfs_scrub_walk_agfl(
/* Superblock */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_superblock_xref(
+ struct xfs_scrub_context *sc,
+ struct xfs_buf *bp)
+{
+}
+
/*
* Scrub the filesystem superblock.
*
@@ -370,11 +381,23 @@ xfs_scrub_superblock(
BBTOB(bp->b_length) - sizeof(struct xfs_dsb)))
xfs_scrub_block_set_corrupt(sc, bp);
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_superblock_xref(sc, bp);
+out:
return error;
}
/* AGF */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agf_xref(
+ struct xfs_scrub_context *sc)
+{
+}
+
/* Scrub the AGF. */
int
xfs_scrub_agf(
@@ -453,6 +476,10 @@ xfs_scrub_agf(
if (agfl_count != 0 && fl_count != agfl_count)
xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp);
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_agf_xref(sc);
out:
return error;
}
@@ -465,6 +492,14 @@ struct xfs_scrub_agfl_info {
xfs_agblock_t *entries;
};
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agfl_block_xref(
+ struct xfs_scrub_context *sc,
+ xfs_agblock_t bno)
+{
+}
+
/* Scrub an AGFL block. */
STATIC int
xfs_scrub_agfl_block(
@@ -482,6 +517,11 @@ xfs_scrub_agfl_block(
else
xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp);
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_agfl_block_xref(sc, agbno);
+out:
return 0;
}
@@ -496,6 +536,13 @@ xfs_scrub_agblock_cmp(
return (int)*a - (int)*b;
}
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agfl_xref(
+ struct xfs_scrub_context *sc)
+{
+}
+
/* Scrub the AGFL. */
int
xfs_scrub_agfl(
@@ -516,6 +563,11 @@ xfs_scrub_agfl(
if (!sc->sa.agf_bp)
return -EFSCORRUPTED;
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_agfl_xref(sc);
+
/* Allocate buffer to ensure uniqueness of AGFL entries. */
agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
agflcount = be32_to_cpu(agf->agf_flcount);
@@ -558,6 +610,13 @@ xfs_scrub_agfl(
/* AGI */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agi_xref(
+ struct xfs_scrub_context *sc)
+{
+}
+
/* Scrub the AGI. */
int
xfs_scrub_agi(
@@ -636,6 +695,10 @@ xfs_scrub_agi(
if (agi->agi_pad32 != cpu_to_be32(0))
xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_agi_xref(sc);
out:
return error;
}
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 059663e..0d95b84 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -50,6 +50,15 @@ xfs_scrub_setup_ag_allocbt(
/* Free space btree scrubber. */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_allocbt_xref(
+ struct xfs_scrub_context *sc,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+}
+
/* Scrub a bnobt/cntbt record. */
STATIC int
xfs_scrub_allocbt_rec(
@@ -70,6 +79,11 @@ xfs_scrub_allocbt_rec(
!xfs_verify_agbno(mp, agno, bno + len - 1))
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
+ if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_allocbt_xref(bs->sc, bno, len);
+out:
return error;
}
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 42fec0b..684f08b 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -99,6 +99,26 @@ struct xfs_scrub_bmap_info {
int whichfork;
};
+/* Cross-reference a single rtdev extent record. */
+STATIC void
+xfs_scrub_bmap_rt_extent_xref(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_inode *ip,
+ struct xfs_btree_cur *cur,
+ struct xfs_bmbt_irec *irec)
+{
+}
+
+/* Cross-reference a single datadev extent record. */
+STATIC void
+xfs_scrub_bmap_extent_xref(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_inode *ip,
+ struct xfs_btree_cur *cur,
+ struct xfs_bmbt_irec *irec)
+{
+}
+
/* Scrub a single extent record. */
STATIC int
xfs_scrub_bmap_extent(
@@ -158,6 +178,14 @@ xfs_scrub_bmap_extent(
xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
+ if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ if (info->is_rt)
+ xfs_scrub_bmap_rt_extent_xref(info, ip, cur, irec);
+ else
+ xfs_scrub_bmap_extent_xref(info, ip, cur, irec);
+out:
info->lastoff = irec->br_startoff + irec->br_blockcount;
return error;
}
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 496d6f2..599d62a 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -58,6 +58,17 @@ xfs_scrub_setup_ag_iallocbt(
/* Inode btree scrubber. */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_iallocbt_chunk_xref(
+ struct xfs_scrub_context *sc,
+ struct xfs_inobt_rec_incore *irec,
+ xfs_agino_t agino,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+}
+
/* Is this chunk worth checking? */
STATIC bool
xfs_scrub_iallocbt_chunk(
@@ -76,6 +87,11 @@ xfs_scrub_iallocbt_chunk(
!xfs_verify_agbno(mp, agno, bno + len - 1))
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
+ if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len);
+out:
return true;
}
@@ -303,7 +319,6 @@ xfs_scrub_iallocbt_rec(
error = xfs_scrub_iallocbt_check_freemask(bs, &irec);
if (error)
goto out;
-
out:
return error;
}
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 2be4b25..e31d981 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -577,6 +577,15 @@ xfs_scrub_inode_map_raw(
return error;
}
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_inode_xref(
+ struct xfs_scrub_context *sc,
+ xfs_ino_t ino,
+ struct xfs_dinode *dip)
+{
+}
+
/* Scrub an inode. */
int
xfs_scrub_inode(
@@ -626,6 +635,10 @@ xfs_scrub_inode(
xfs_scrub_ino_set_preen(sc, ino, bp);
}
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_inode_xref(sc, ino, dip);
out:
if (bp)
xfs_trans_brelse(sc->tp, bp);
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 2f88a8d..5a3aa9b 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -50,6 +50,16 @@ xfs_scrub_setup_ag_refcountbt(
/* Reference count btree scrubber. */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_refcountbt_xref(
+ struct xfs_scrub_context *sc,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ xfs_nlink_t refcount)
+{
+}
+
/* Scrub a refcountbt record. */
STATIC int
xfs_scrub_refcountbt_rec(
@@ -83,6 +93,11 @@ xfs_scrub_refcountbt_rec(
if (refcount == 0)
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
+ if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_refcountbt_xref(bs->sc, bno, len, refcount);
+out:
return error;
}
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 97846c4..80edddb 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -51,6 +51,14 @@ xfs_scrub_setup_ag_rmapbt(
/* Reverse-mapping scrubber. */
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_rmapbt_xref(
+ struct xfs_scrub_context *sc,
+ struct xfs_rmap_irec *irec)
+{
+}
+
/* Scrub an rmapbt record. */
STATIC int
xfs_scrub_rmapbt_rec(
@@ -121,6 +129,11 @@ xfs_scrub_rmapbt_rec(
irec.rm_owner > XFS_RMAP_OWN_FS)
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
}
+
+ if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ goto out;
+
+ xfs_scrub_rmapbt_xref(bs->sc, &irec);
out:
return error;
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 08/15] xfs: cross-reference with the bnobt
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (6 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 07/15] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 09/15] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
When we're scrubbing various btrees, cross-reference the records with
the bnobt to ensure that we don't also think the space is free.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 84 +++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/alloc.c | 19 +++++++++++
fs/xfs/scrub/bmap.c | 21 +++++++++++-
fs/xfs/scrub/btree.c | 12 +++++++
fs/xfs/scrub/ialloc.c | 1 +
fs/xfs/scrub/inode.c | 15 ++++++++
fs/xfs/scrub/refcount.c | 1 +
fs/xfs/scrub/rmap.c | 4 ++
fs/xfs/scrub/scrub.h | 5 +++
9 files changed, 161 insertions(+), 1 deletion(-)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index b4297e9..6241475 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -107,6 +107,20 @@ xfs_scrub_superblock_xref(
struct xfs_scrub_context *sc,
struct xfs_buf *bp)
{
+ struct xfs_mount *mp = sc->mp;
+ xfs_agnumber_t agno = sc->sm->sm_agno;
+ xfs_agblock_t bno;
+ int error;
+
+ bno = XFS_SB_BLOCK(mp);
+
+ error = xfs_scrub_ag_init(sc, agno, &sc->sa);
+ if (!xfs_scrub_xref_process_error(sc, agno, bno, &error))
+ return;
+
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+
+ /* scrub teardown will take care of sc->sa for us */
}
/*
@@ -391,11 +405,51 @@ xfs_scrub_superblock(
/* AGF */
+/* Tally freespace record lengths. */
+STATIC int
+xfs_scrub_agf_record_bno_lengths(
+ struct xfs_btree_cur *cur,
+ struct xfs_alloc_rec_incore *rec,
+ void *priv)
+{
+ xfs_extlen_t *blocks = priv;
+
+ (*blocks) += rec->ar_blockcount;
+ return 0;
+}
+
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_agf_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_mount *mp = sc->mp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
+ struct xfs_btree_cur **pcur;
+ xfs_agblock_t bno;
+ xfs_extlen_t blocks;
+ int error;
+
+ bno = XFS_AGF_BLOCK(mp);
+
+ error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+ if (error)
+ return;
+
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+
+ /* Check agf_freeblks */
+ pcur = &sc->sa.bno_cur;
+ if (*pcur) {
+ blocks = 0;
+ error = xfs_alloc_query_all(*pcur,
+ xfs_scrub_agf_record_bno_lengths, &blocks);
+ if (xfs_scrub_should_xref(sc, &error, pcur) &&
+ blocks != be32_to_cpu(agf->agf_freeblks))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+ }
+
+ /* scrub teardown will take care of sc->sa for us */
}
/* Scrub the AGF. */
@@ -498,6 +552,7 @@ xfs_scrub_agfl_block_xref(
struct xfs_scrub_context *sc,
xfs_agblock_t bno)
{
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
}
/* Scrub an AGFL block. */
@@ -541,6 +596,22 @@ STATIC void
xfs_scrub_agfl_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_mount *mp = sc->mp;
+ xfs_agblock_t bno;
+ int error;
+
+ bno = XFS_AGFL_BLOCK(mp);
+
+ error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+ if (error)
+ return;
+
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+
+ /*
+ * Scrub teardown will take care of sc->sa for us. Leave sc->sa
+ * active so that the agfl block xref can use it too.
+ */
}
/* Scrub the AGFL. */
@@ -615,6 +686,19 @@ STATIC void
xfs_scrub_agi_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_mount *mp = sc->mp;
+ xfs_agblock_t bno;
+ int error;
+
+ bno = XFS_AGI_BLOCK(mp);
+
+ error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+ if (error)
+ return;
+
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+
+ /* scrub teardown will take care of sc->sa for us */
}
/* Scrub the AGI. */
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 0d95b84..3d6f8cc 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -114,3 +114,22 @@ xfs_scrub_cntbt(
{
return xfs_scrub_allocbt(sc, XFS_BTNUM_CNT);
}
+
+/* xref check that the extent is not free */
+void
+xfs_scrub_xref_not_free(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ bool is_freesp;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ error = xfs_alloc_has_record(*pcur, bno, len, &is_freesp);
+ if (xfs_scrub_should_xref(sc, &error, pcur) && is_freesp)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 684f08b..7ce135b 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -31,12 +31,12 @@
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_inode_fork.h"
-#include "xfs_alloc.h"
#include "xfs_rtalloc.h"
#include "xfs_bmap.h"
#include "xfs_bmap_util.h"
#include "xfs_bmap_btree.h"
#include "xfs_rmap.h"
+#include "xfs_alloc.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -117,6 +117,25 @@ xfs_scrub_bmap_extent_xref(
struct xfs_btree_cur *cur,
struct xfs_bmbt_irec *irec)
{
+ struct xfs_scrub_ag sa = { 0 };
+ struct xfs_mount *mp = info->sc->mp;
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ xfs_extlen_t len;
+ int error;
+
+ agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock);
+ agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock);
+ len = irec->br_blockcount;
+
+ error = xfs_scrub_ag_init(info->sc, agno, &sa);
+ if (!xfs_scrub_fblock_process_error(info->sc, info->whichfork,
+ irec->br_startoff, &error))
+ return;
+
+ xfs_scrub_xref_not_free(info->sc, &sa.bno_cur, agbno, len);
+
+ xfs_scrub_ag_free(info->sc, &sa);
}
/* Scrub a single extent record. */
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 40fc26f..67b6248 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -378,9 +378,12 @@ xfs_scrub_btree_check_block_owner(
struct xfs_scrub_ag sa = { 0 };
struct xfs_scrub_ag *psa;
xfs_agnumber_t agno;
+ xfs_agblock_t bno;
+ bool is_freesp;
int error = 0;
agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
+ bno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) {
error = xfs_scrub_ag_init(bs->sc, agno, &sa);
@@ -392,6 +395,15 @@ xfs_scrub_btree_check_block_owner(
psa = &bs->sc->sa;
}
+ /* Cross-reference with the bnobt. */
+ if (psa->bno_cur) {
+ error = xfs_alloc_has_record(psa->bno_cur, bno, 1, &is_freesp);
+ if (xfs_scrub_should_xref(bs->sc, &error, &psa->bno_cur) &&
+ is_freesp)
+ xfs_scrub_btree_xref_set_corrupt(bs->sc, psa->bno_cur,
+ 0);
+ }
+
if (psa == &sa)
xfs_scrub_ag_free(bs->sc, &sa);
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 599d62a..4c4ef17c 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -67,6 +67,7 @@ xfs_scrub_iallocbt_chunk_xref(
xfs_agblock_t bno,
xfs_extlen_t len)
{
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
}
/* Is this chunk worth checking? */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index e31d981..e6038c1 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -584,6 +584,21 @@ xfs_scrub_inode_xref(
xfs_ino_t ino,
struct xfs_dinode *dip)
{
+ struct xfs_scrub_ag sa = { 0 };
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ int error;
+
+ agno = XFS_INO_TO_AGNO(sc->mp, ino);
+ agbno = XFS_INO_TO_AGBNO(sc->mp, ino);
+
+ error = xfs_scrub_ag_init(sc, agno, &sa);
+ if (!xfs_scrub_xref_process_error(sc, agno, agbno, &error))
+ return;
+
+ xfs_scrub_xref_not_free(sc, &sa.bno_cur, agbno, 1);
+
+ xfs_scrub_ag_free(sc, &sa);
}
/* Scrub an inode. */
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 5a3aa9b..19c303d 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -58,6 +58,7 @@ xfs_scrub_refcountbt_xref(
xfs_extlen_t len,
xfs_nlink_t refcount)
{
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
}
/* Scrub a refcountbt record. */
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 80edddb..5c9646b 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -57,6 +57,10 @@ xfs_scrub_rmapbt_xref(
struct xfs_scrub_context *sc,
struct xfs_rmap_irec *irec)
{
+ xfs_agblock_t bno = irec->rm_startblock;
+ xfs_extlen_t len = irec->rm_blockcount;
+
+ xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
}
/* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 2a79614..e75ff0e 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -123,4 +123,9 @@ xfs_scrub_quota(struct xfs_scrub_context *sc)
}
#endif
+/* cross-referencing helpers */
+void xfs_scrub_xref_not_free(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
+
#endif /* __XFS_SCRUB_SCRUB_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 09/15] xfs: cross-reference bnobt records with cntbt
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (7 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 08/15] xfs: cross-reference with the bnobt Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 10/15] xfs: cross-reference inode btrees during scrub Darrick J. Wong
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Scrub should make sure that each bnobt record has a corresponding
cntbt record.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 26 ++++++++++++++++++++++++++
fs/xfs/scrub/alloc.c | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 6241475..c7bfa20 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -428,6 +428,7 @@ xfs_scrub_agf_xref(
struct xfs_btree_cur **pcur;
xfs_agblock_t bno;
xfs_extlen_t blocks;
+ int have;
int error;
bno = XFS_AGF_BLOCK(mp);
@@ -449,6 +450,31 @@ xfs_scrub_agf_xref(
xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
}
+ /* Cross-reference with the cntbt. */
+ pcur = &sc->sa.cnt_cur;
+ while (*pcur) {
+ xfs_agblock_t agbno;
+
+ /* Any freespace at all? */
+ error = xfs_alloc_lookup_le(*pcur, 0, -1U, &have);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ break;
+ if (!have) {
+ if (agf->agf_freeblks != be32_to_cpu(0))
+ xfs_scrub_block_xref_set_corrupt(sc,
+ sc->sa.agf_bp);
+ break;
+ }
+
+ /* Check agf_longest */
+ error = xfs_alloc_get_rec(*pcur, &agbno, &blocks, &have);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ break;
+ if (!have || blocks != be32_to_cpu(agf->agf_longest))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+ break;
+ }
+
/* scrub teardown will take care of sc->sa for us */
}
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 3d6f8cc..9a28e3d 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -31,6 +31,7 @@
#include "xfs_sb.h"
#include "xfs_alloc.h"
#include "xfs_rmap.h"
+#include "xfs_alloc.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -57,6 +58,42 @@ xfs_scrub_allocbt_xref(
xfs_agblock_t bno,
xfs_extlen_t len)
{
+ struct xfs_btree_cur **pcur;
+ struct xfs_scrub_ag *psa = &sc->sa;
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+ int has_otherrec;
+ int error;
+
+ /*
+ * Ensure there's a corresponding cntbt/bnobt record matching
+ * this bnobt/cntbt record, respectively.
+ */
+ if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
+ pcur = &psa->cnt_cur;
+ else
+ pcur = &psa->bno_cur;
+ while (*pcur) {
+ error = xfs_alloc_lookup_le(*pcur, bno, len, &has_otherrec);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ break;
+ if (!has_otherrec) {
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ break;
+ }
+
+ error = xfs_alloc_get_rec(*pcur, &fbno, &flen, &has_otherrec);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ break;
+ if (!has_otherrec) {
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ break;
+ }
+
+ if (fbno != bno || flen != len)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ break;
+ }
}
/* Scrub a bnobt/cntbt record. */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 10/15] xfs: cross-reference inode btrees during scrub
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (8 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 09/15] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 11/15] xfs: cross-reference reverse-mapping btree Darrick J. Wong
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Cross-reference the inode btrees with the other metadata when we
scrub the filesystem.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 25 +++++++++++++++++++
fs/xfs/scrub/alloc.c | 3 ++
fs/xfs/scrub/bmap.c | 2 +
fs/xfs/scrub/ialloc.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/inode.c | 2 +
fs/xfs/scrub/refcount.c | 2 +
fs/xfs/scrub/rmap.c | 6 ++++
fs/xfs/scrub/scrub.h | 6 ++++
8 files changed, 109 insertions(+)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index c7bfa20..1c2d521 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -119,6 +119,8 @@ xfs_scrub_superblock_xref(
return;
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
/* scrub teardown will take care of sc->sa for us */
}
@@ -475,6 +477,9 @@ xfs_scrub_agf_xref(
break;
}
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+
/* scrub teardown will take care of sc->sa for us */
}
@@ -579,6 +584,8 @@ xfs_scrub_agfl_block_xref(
xfs_agblock_t bno)
{
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
}
/* Scrub an AGFL block. */
@@ -633,6 +640,8 @@ xfs_scrub_agfl_xref(
return;
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
/*
* Scrub teardown will take care of sc->sa for us. Leave sc->sa
@@ -713,7 +722,11 @@ xfs_scrub_agi_xref(
struct xfs_scrub_context *sc)
{
struct xfs_mount *mp = sc->mp;
+ struct xfs_btree_cur **pcur;
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
xfs_agblock_t bno;
+ xfs_agino_t icount;
+ xfs_agino_t freecount;
int error;
bno = XFS_AGI_BLOCK(mp);
@@ -723,6 +736,18 @@ xfs_scrub_agi_xref(
return;
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+
+ /* Check agi_count/agi_freecount */
+ pcur = &sc->sa.ino_cur;
+ if (*pcur) {
+ error = xfs_ialloc_count_inodes(*pcur, &icount, &freecount);
+ if (xfs_scrub_should_xref(sc, &error, pcur) &&
+ (be32_to_cpu(agi->agi_count) != icount ||
+ be32_to_cpu(agi->agi_freecount) != freecount))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agi_bp);
+ }
/* scrub teardown will take care of sc->sa for us */
}
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 9a28e3d..cd709f4 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -94,6 +94,9 @@ xfs_scrub_allocbt_xref(
xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
break;
}
+
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
}
/* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 7ce135b..a0274d3 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -134,6 +134,8 @@ xfs_scrub_bmap_extent_xref(
return;
xfs_scrub_xref_not_free(info->sc, &sa.bno_cur, agbno, len);
+ xfs_scrub_xref_not_inodes(info->sc, &sa.ino_cur, agbno, len);
+ xfs_scrub_xref_not_inodes(info->sc, &sa.fino_cur, agbno, len);
xfs_scrub_ag_free(info->sc, &sa);
}
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 4c4ef17c..da07393 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -67,7 +67,29 @@ xfs_scrub_iallocbt_chunk_xref(
xfs_agblock_t bno,
xfs_extlen_t len)
{
+ struct xfs_btree_cur **pcur;
+ bool has_irec;
+ int error;
+
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
+
+ /*
+ * If we're checking the finobt, cross-reference with the inobt.
+ * Otherwise we're checking the inobt; if there is an finobt,
+ * make sure we have a record or not depending on freecount.
+ */
+ if (sc->sm->sm_type == XFS_SCRUB_TYPE_FINOBT)
+ pcur = &sc->sa.ino_cur;
+ else
+ pcur = &sc->sa.fino_cur;
+ if (*pcur) {
+ error = xfs_ialloc_has_inode_record(*pcur,
+ agino, agino, &has_irec);
+ if (xfs_scrub_should_xref(sc, &error, pcur) &&
+ ((irec->ir_freecount > 0 && !has_irec) ||
+ (irec->ir_freecount == 0 && has_irec)))
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ }
}
/* Is this chunk worth checking? */
@@ -351,3 +373,44 @@ xfs_scrub_finobt(
{
return xfs_scrub_iallocbt(sc, XFS_BTNUM_FINO);
}
+
+static inline void
+__xfs_scrub_xref_check_inodes(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool fs_ok)
+{
+ bool has_inodes;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ error = xfs_ialloc_has_inodes_at_extent(*pcur, bno, len, &has_inodes);
+ if (xfs_scrub_should_xref(sc, &error, pcur) && has_inodes != fs_ok)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
+
+/* xref check that the extent is not covered by inodes */
+void
+xfs_scrub_xref_not_inodes(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ __xfs_scrub_xref_check_inodes(sc, pcur, bno, len, false);
+}
+
+/* xref check that the extent is covered by inodes */
+void
+xfs_scrub_xref_are_inodes(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ __xfs_scrub_xref_check_inodes(sc, pcur, bno, len, true);
+}
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index e6038c1..bb5172c 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -597,6 +597,8 @@ xfs_scrub_inode_xref(
return;
xfs_scrub_xref_not_free(sc, &sa.bno_cur, agbno, 1);
+ xfs_scrub_xref_are_inodes(sc, &sc->sa.ino_cur, agbno, 1);
+ xfs_scrub_xref_are_inodes(sc, &sc->sa.fino_cur, agbno, 1);
xfs_scrub_ag_free(sc, &sa);
}
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 19c303d..8add281 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -59,6 +59,8 @@ xfs_scrub_refcountbt_xref(
xfs_nlink_t refcount)
{
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
}
/* Scrub a refcountbt record. */
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 5c9646b..8d49556 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -61,6 +61,12 @@ xfs_scrub_rmapbt_xref(
xfs_extlen_t len = irec->rm_blockcount;
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
+ if (irec->rm_owner == XFS_RMAP_OWN_INODES) {
+ xfs_scrub_xref_are_inodes(sc, &sc->sa.ino_cur, bno, len);
+ } else {
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
+ xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
+ }
}
/* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index e75ff0e..768df35 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -127,5 +127,11 @@ xfs_scrub_quota(struct xfs_scrub_context *sc)
void xfs_scrub_xref_not_free(struct xfs_scrub_context *sc,
struct xfs_btree_cur **pcur, xfs_agblock_t bno,
xfs_extlen_t len);
+void xfs_scrub_xref_not_inodes(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
+void xfs_scrub_xref_are_inodes(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
#endif /* __XFS_SCRUB_SCRUB_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 11/15] xfs: cross-reference reverse-mapping btree
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (9 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 10/15] xfs: cross-reference inode btrees during scrub Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 12/15] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
When scrubbing various btrees, we should cross-reference the records
with the reverse mapping btree and ensure that traversing the btree
finds the same number of blocks that the rmapbt thinks are owned by
that btree.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 70 +++++++++++++++++++++++++++++++-
fs/xfs/scrub/alloc.c | 1
fs/xfs/scrub/bmap.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/btree.c | 2 +
fs/xfs/scrub/common.c | 47 ++++++++++++++++++++++
fs/xfs/scrub/common.h | 4 ++
fs/xfs/scrub/ialloc.c | 74 ++++++++++++++++++++++++++++++++++
fs/xfs/scrub/inode.c | 4 ++
fs/xfs/scrub/rmap.c | 64 +++++++++++++++++++++++++++++
fs/xfs/scrub/scrub.h | 9 ++++
10 files changed, 374 insertions(+), 3 deletions(-)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 1c2d521..3e5bb78 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -32,6 +32,7 @@
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
+#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -107,6 +108,7 @@ xfs_scrub_superblock_xref(
struct xfs_scrub_context *sc,
struct xfs_buf *bp)
{
+ struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp;
xfs_agnumber_t agno = sc->sm->sm_agno;
xfs_agblock_t bno;
@@ -121,6 +123,8 @@ xfs_scrub_superblock_xref(
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
/* scrub teardown will take care of sc->sa for us */
}
@@ -420,11 +424,59 @@ xfs_scrub_agf_record_bno_lengths(
return 0;
}
+/* Check the btree block counts in the AGF against the btrees. */
+STATIC void
+xfs_scrub_agf_xref_btreeblks(
+ struct xfs_scrub_context *sc)
+{
+ struct xfs_btree_cur **pcur;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
+ struct xfs_mount *mp = sc->mp;
+ xfs_agblock_t blocks;
+ xfs_agblock_t btreeblks;
+ int error;
+
+ /* Check agf_rmap_blocks; set up for agf_btreeblks check */
+ pcur = &sc->sa.rmap_cur;
+ if (*pcur) {
+ error = xfs_btree_count_blocks(*pcur, &blocks);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ return;
+ btreeblks = blocks - 1;
+ if (blocks != be32_to_cpu(agf->agf_rmap_blocks))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+ } else {
+ btreeblks = 0;
+ }
+
+ /*
+ * No rmap cursor; we can't xref if we have the rmapbt feature.
+ * We also can't do it if we're missing the free space btree cursors.
+ */
+ if ((xfs_sb_version_hasrmapbt(&mp->m_sb) && !sc->sa.rmap_cur) ||
+ !sc->sa.bno_cur || !sc->sa.cnt_cur)
+ return;
+
+ /* Check agf_btreeblks */
+ error = xfs_btree_count_blocks(sc->sa.bno_cur, &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.bno_cur))
+ btreeblks += blocks - 1;
+
+ error = xfs_btree_count_blocks(sc->sa.cnt_cur, &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.cnt_cur))
+ btreeblks += blocks - 1;
+
+ if (sc->sa.bno_cur && sc->sa.cnt_cur &&
+ btreeblks != be32_to_cpu(agf->agf_btreeblks))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+}
+
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_agf_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp;
struct xfs_agf *agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
struct xfs_btree_cur **pcur;
@@ -479,6 +531,9 @@ xfs_scrub_agf_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
+ xfs_scrub_agf_xref_btreeblks(sc);
/* scrub teardown will take care of sc->sa for us */
}
@@ -572,6 +627,7 @@ xfs_scrub_agf(
/* AGFL */
struct xfs_scrub_agfl_info {
+ struct xfs_owner_info oinfo;
unsigned int sz_entries;
unsigned int nr_entries;
xfs_agblock_t *entries;
@@ -581,11 +637,13 @@ struct xfs_scrub_agfl_info {
STATIC void
xfs_scrub_agfl_block_xref(
struct xfs_scrub_context *sc,
- xfs_agblock_t bno)
+ xfs_agblock_t bno,
+ struct xfs_owner_info *oinfo)
{
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, oinfo);
}
/* Scrub an AGFL block. */
@@ -608,7 +666,7 @@ xfs_scrub_agfl_block(
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
goto out;
- xfs_scrub_agfl_block_xref(sc, agbno);
+ xfs_scrub_agfl_block_xref(sc, agbno, priv);
out:
return 0;
}
@@ -629,6 +687,7 @@ STATIC void
xfs_scrub_agfl_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp;
xfs_agblock_t bno;
int error;
@@ -642,6 +701,8 @@ xfs_scrub_agfl_xref(
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
/*
* Scrub teardown will take care of sc->sa for us. Leave sc->sa
@@ -689,6 +750,7 @@ xfs_scrub_agfl(
}
/* Check the blocks in the AGFL. */
+ xfs_rmap_ag_owner(&sai.oinfo, XFS_RMAP_OWN_AG);
error = xfs_scrub_walk_agfl(sc, xfs_scrub_agfl_block, &sai);
if (error)
goto out_free;
@@ -721,6 +783,7 @@ STATIC void
xfs_scrub_agi_xref(
struct xfs_scrub_context *sc)
{
+ struct xfs_owner_info oinfo;
struct xfs_mount *mp = sc->mp;
struct xfs_btree_cur **pcur;
struct xfs_agi *agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
@@ -749,6 +812,9 @@ xfs_scrub_agi_xref(
xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agi_bp);
}
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
+
/* scrub teardown will take care of sc->sa for us */
}
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index cd709f4..a4046c1 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -97,6 +97,7 @@ xfs_scrub_allocbt_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
+ xfs_scrub_xref_no_rmap(sc, &sc->sa.rmap_cur, bno, len);
}
/* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index a0274d3..f408193 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -99,6 +99,107 @@ struct xfs_scrub_bmap_info {
int whichfork;
};
+/* Make sure that we have rmapbt records for this extent. */
+STATIC void
+xfs_scrub_bmap_xref_rmap(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_scrub_ag *sa,
+ struct xfs_bmbt_irec *irec,
+ xfs_fsblock_t bno)
+{
+ struct xfs_rmap_irec rmap;
+ uint64_t owner;
+ xfs_fileoff_t offset;
+ unsigned long long rmap_end;
+ unsigned int rflags;
+ int has_rmap;
+ int error;
+
+ if (!sa->rmap_cur)
+ return;
+
+ if (info->whichfork == XFS_COW_FORK) {
+ owner = XFS_RMAP_OWN_COW;
+ offset = 0;
+ } else {
+ owner = info->sc->ip->i_ino;
+ offset = irec->br_startoff;
+ }
+
+ /* Look for a corresponding rmap. */
+ rflags = 0;
+ if (info->whichfork == XFS_ATTR_FORK)
+ rflags |= XFS_RMAP_ATTR_FORK;
+
+ if (info->is_shared) {
+ error = xfs_rmap_lookup_le_range(sa->rmap_cur, bno, owner,
+ offset, rflags, &rmap,
+ &has_rmap);
+ if (!xfs_scrub_should_xref(info->sc, &error, &sa->rmap_cur))
+ return;
+ if (!has_rmap) {
+ xfs_scrub_fblock_xref_set_corrupt(info->sc,
+ info->whichfork, irec->br_startoff);
+ return;
+ }
+ } else {
+ error = xfs_rmap_lookup_le(sa->rmap_cur, bno, 0, owner,
+ offset, rflags, &has_rmap);
+ if (!xfs_scrub_should_xref(info->sc, &error, &sa->rmap_cur))
+ return;
+ if (!has_rmap) {
+ xfs_scrub_fblock_xref_set_corrupt(info->sc,
+ info->whichfork, irec->br_startoff);
+ return;
+ }
+
+ error = xfs_rmap_get_rec(sa->rmap_cur, &rmap, &has_rmap);
+ if (!xfs_scrub_should_xref(info->sc, &error, &sa->rmap_cur))
+ return;
+ if (!has_rmap) {
+ xfs_scrub_fblock_xref_set_corrupt(info->sc,
+ info->whichfork, irec->br_startoff);
+ return;
+ }
+ }
+
+ /* Check the rmap. */
+ rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
+ if (rmap.rm_startblock > bno ||
+ bno + irec->br_blockcount > rmap_end)
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+
+ if (owner != XFS_RMAP_OWN_COW) {
+ rmap_end = (unsigned long long)rmap.rm_offset +
+ rmap.rm_blockcount;
+ if (rmap.rm_offset > offset ||
+ offset + irec->br_blockcount > rmap_end)
+ xfs_scrub_fblock_xref_set_corrupt(info->sc,
+ info->whichfork, irec->br_startoff);
+ } else {
+ /*
+ * We don't set the unwritten flag for CoW
+ * staging extent rmaps; everything is unwritten.
+ */
+ irec->br_state = XFS_EXT_NORM;
+ }
+ if (rmap.rm_owner != owner)
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+ if (irec->br_state == XFS_EXT_UNWRITTEN &&
+ !(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+ if (info->whichfork == XFS_ATTR_FORK &&
+ !(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+ if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+}
+
/* Cross-reference a single rtdev extent record. */
STATIC void
xfs_scrub_bmap_rt_extent_xref(
@@ -136,6 +237,7 @@ xfs_scrub_bmap_extent_xref(
xfs_scrub_xref_not_free(info->sc, &sa.bno_cur, agbno, len);
xfs_scrub_xref_not_inodes(info->sc, &sa.ino_cur, agbno, len);
xfs_scrub_xref_not_inodes(info->sc, &sa.fino_cur, agbno, len);
+ xfs_scrub_bmap_xref_rmap(info, &sa, irec, agbno);
xfs_scrub_ag_free(info->sc, &sa);
}
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 67b6248..26149cb 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -404,6 +404,8 @@ xfs_scrub_btree_check_block_owner(
0);
}
+ xfs_scrub_xref_owned_by(bs->sc, &psa->rmap_cur, bno, 1, bs->oinfo);
+
if (psa == &sa)
xfs_scrub_ag_free(bs->sc, &sa);
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index effdcfa..c36a91b 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -325,6 +325,53 @@ xfs_scrub_set_incomplete(
}
/*
+ * rmap scrubbing -- compute the number of blocks with a given owner,
+ * at least according to the reverse mapping data.
+ */
+
+struct xfs_scrub_rmap_ownedby_info {
+ struct xfs_owner_info *oinfo;
+ xfs_filblks_t *blocks;
+};
+
+STATIC int
+xfs_scrub_count_rmap_ownedby_helper(
+ struct xfs_btree_cur *cur,
+ struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xfs_scrub_rmap_ownedby_info *sroi = priv;
+
+ if (rec->rm_owner == sroi->oinfo->oi_owner &&
+ (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
+ !!(rec->rm_flags & XFS_RMAP_ATTR_FORK) ==
+ !!(sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK)))
+ (*sroi->blocks) += rec->rm_blockcount;
+ return 0;
+}
+
+/*
+ * Calculate the number of blocks the rmap thinks are owned by something.
+ * The caller should pass us an rmapbt cursor.
+ */
+int
+xfs_scrub_count_rmap_ownedby_ag(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ struct xfs_owner_info *oinfo,
+ xfs_filblks_t *blocks)
+{
+ struct xfs_scrub_rmap_ownedby_info sroi;
+
+ sroi.oinfo = oinfo;
+ *blocks = 0;
+ sroi.blocks = blocks;
+
+ return xfs_rmap_query_all(cur, xfs_scrub_count_rmap_ownedby_helper,
+ &sroi);
+}
+
+/*
* AG scrubbing
*
* These helpers facilitate locking an allocation group's header
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index 0e12a10..4f6a6e1 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -148,6 +148,10 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
int (*fn)(struct xfs_scrub_context *, xfs_agblock_t bno,
void *),
void *priv);
+int xfs_scrub_count_rmap_ownedby_ag(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur *cur,
+ struct xfs_owner_info *oinfo,
+ xfs_filblks_t *blocks);
int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc,
struct xfs_inode *ip, bool force_log);
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index da07393..d4b7f13 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -67,6 +67,7 @@ xfs_scrub_iallocbt_chunk_xref(
xfs_agblock_t bno,
xfs_extlen_t len)
{
+ struct xfs_owner_info oinfo;
struct xfs_btree_cur **pcur;
bool has_irec;
int error;
@@ -90,6 +91,9 @@ xfs_scrub_iallocbt_chunk_xref(
(irec->ir_freecount == 0 && has_irec)))
xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
}
+
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, len, &oinfo);
}
/* Is this chunk worth checking? */
@@ -228,6 +232,14 @@ xfs_scrub_iallocbt_check_freemask(
continue;
}
+ if (ir_holemask == 0)
+ xfs_scrub_xref_owned_by(bs->sc, &bs->sc->sa.rmap_cur,
+ agbno, blks_per_cluster, &oinfo);
+ else
+ xfs_scrub_xref_not_owned_by(bs->sc,
+ &bs->sc->sa.rmap_cur,
+ agbno, blks_per_cluster, &oinfo);
+
/* If any part of this is a hole, skip it. */
if (ir_holemask)
continue;
@@ -266,6 +278,7 @@ xfs_scrub_iallocbt_rec(
union xfs_btree_rec *rec)
{
struct xfs_mount *mp = bs->cur->bc_mp;
+ xfs_filblks_t *inode_blocks = bs->private;
struct xfs_inobt_rec_incore irec;
uint64_t holes;
xfs_agnumber_t agno = bs->cur->bc_private.a.agno;
@@ -302,6 +315,8 @@ xfs_scrub_iallocbt_rec(
if ((agbno & (xfs_ialloc_cluster_alignment(mp) - 1)) ||
(agbno & (xfs_icluster_size_fsb(mp) - 1)))
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
+ *inode_blocks += XFS_B_TO_FSB(mp,
+ irec.ir_count * mp->m_sb.sb_inodesize);
/* Handle non-sparse inodes */
if (!xfs_inobt_issparse(irec.ir_holemask)) {
@@ -346,6 +361,53 @@ xfs_scrub_iallocbt_rec(
return error;
}
+/*
+ * Make sure the inode btrees are as large as the rmap thinks they are.
+ * Don't bother if we're missing btree cursors, as we're already corrupt.
+ */
+STATIC void
+xfs_scrub_iallocbt_xref_rmap(
+ struct xfs_scrub_context *sc,
+ int which,
+ struct xfs_owner_info *oinfo,
+ xfs_filblks_t inode_blocks)
+{
+ xfs_filblks_t blocks;
+ xfs_extlen_t inobt_blocks = 0;
+ xfs_extlen_t finobt_blocks = 0;
+ int error;
+
+ if (!sc->sa.ino_cur || !sc->sa.rmap_cur)
+ return;
+
+ /* Check that we saw as many inobt blocks as the rmap says. */
+ error = xfs_btree_count_blocks(sc->sa.ino_cur, &inobt_blocks);
+ if (error)
+ return;
+
+ if (xfs_sb_version_hasfinobt(&sc->mp->m_sb)) {
+ if (!sc->sa.fino_cur)
+ return;
+ error = xfs_btree_count_blocks(sc->sa.fino_cur, &finobt_blocks);
+ if (error)
+ return;
+ }
+
+ error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, oinfo,
+ &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.rmap_cur) &&
+ blocks != inobt_blocks + finobt_blocks)
+ xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
+
+ /* Check that we saw as many inode blocks as the rmap knows about. */
+ xfs_rmap_ag_owner(oinfo, XFS_RMAP_OWN_INODES);
+ error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, oinfo,
+ &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.rmap_cur) &&
+ blocks != inode_blocks)
+ xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
+}
+
/* Scrub the inode btrees for some AG. */
STATIC int
xfs_scrub_iallocbt(
@@ -354,10 +416,20 @@ xfs_scrub_iallocbt(
{
struct xfs_btree_cur *cur;
struct xfs_owner_info oinfo;
+ xfs_filblks_t inode_blocks = 0;
+ int error;
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INOBT);
cur = which == XFS_BTNUM_INO ? sc->sa.ino_cur : sc->sa.fino_cur;
- return xfs_scrub_btree(sc, cur, xfs_scrub_iallocbt_rec, &oinfo, NULL);
+ error = xfs_scrub_btree(sc, cur, xfs_scrub_iallocbt_rec, &oinfo,
+ &inode_blocks);
+ if (error)
+ return error;
+
+ if (which == XFS_BTNUM_INO)
+ xfs_scrub_iallocbt_xref_rmap(sc, which, &oinfo, inode_blocks);
+
+ return error;
}
int
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index bb5172c..830d44a 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -36,6 +36,7 @@
#include "xfs_ialloc.h"
#include "xfs_da_format.h"
#include "xfs_reflink.h"
+#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -585,6 +586,7 @@ xfs_scrub_inode_xref(
struct xfs_dinode *dip)
{
struct xfs_scrub_ag sa = { 0 };
+ struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
int error;
@@ -599,6 +601,8 @@ xfs_scrub_inode_xref(
xfs_scrub_xref_not_free(sc, &sa.bno_cur, agbno, 1);
xfs_scrub_xref_are_inodes(sc, &sc->sa.ino_cur, agbno, 1);
xfs_scrub_xref_are_inodes(sc, &sc->sa.fino_cur, agbno, 1);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
+ xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, agbno, 1, &oinfo);
xfs_scrub_ag_free(sc, &sa);
}
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 8d49556..7970e73 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -159,3 +159,67 @@ xfs_scrub_rmapbt(
return xfs_scrub_btree(sc, sc->sa.rmap_cur, xfs_scrub_rmapbt_rec,
&oinfo, NULL);
}
+
+/* xref check that the extent is owned by a given owner */
+static inline void
+xfs_scrub_xref_check_owner(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ struct xfs_owner_info *oinfo,
+ bool fs_ok)
+{
+ bool has_rmap;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ error = xfs_rmap_record_exists(*pcur, bno, len, oinfo, &has_rmap);
+ if (xfs_scrub_should_xref(sc, &error, pcur) && has_rmap != fs_ok)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
+
+/* xref check that the extent is owned by a given owner */
+void
+xfs_scrub_xref_owned_by(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ struct xfs_owner_info *oinfo)
+{
+ xfs_scrub_xref_check_owner(sc, pcur, bno, len, oinfo, true);
+}
+
+/* xref check that the extent is not owned by a given owner */
+void
+xfs_scrub_xref_not_owned_by(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ struct xfs_owner_info *oinfo)
+{
+ xfs_scrub_xref_check_owner(sc, pcur, bno, len, oinfo, false);
+}
+
+/* xref check that the extent has no reverse mapping at all */
+void
+xfs_scrub_xref_no_rmap(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ bool has_rmap;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ error = xfs_rmap_has_record(*pcur, bno, len, &has_rmap);
+ if (xfs_scrub_should_xref(sc, &error, pcur) && has_rmap)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 768df35..4789dba 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -133,5 +133,14 @@ void xfs_scrub_xref_not_inodes(struct xfs_scrub_context *sc,
void xfs_scrub_xref_are_inodes(struct xfs_scrub_context *sc,
struct xfs_btree_cur **pcur, xfs_agblock_t bno,
xfs_extlen_t len);
+void xfs_scrub_xref_owned_by(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len, struct xfs_owner_info *oinfo);
+void xfs_scrub_xref_not_owned_by(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len, struct xfs_owner_info *oinfo);
+void xfs_scrub_xref_no_rmap(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
#endif /* __XFS_SCRUB_SCRUB_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 12/15] xfs: cross-reference the rmapbt data with the refcountbt
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (10 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 11/15] xfs: cross-reference reverse-mapping btree Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 13/15] xfs: cross-reference refcount btree during scrub Darrick J. Wong
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Cross reference the refcount data with the rmap data to check that the
number of rmaps for a given block match the refcount of that block, and
that CoW blocks (which are owned entirely by the refcountbt) are tracked
as well.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/scrub/refcount.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 313 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 8add281..6efae58 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -50,6 +50,274 @@ xfs_scrub_setup_ag_refcountbt(
/* Reference count btree scrubber. */
+/*
+ * Confirming Reference Counts via Reverse Mappings
+ *
+ * We want to count the reverse mappings overlapping a refcount record
+ * (bno, len, refcount), allowing for the possibility that some of the
+ * overlap may come from smaller adjoining reverse mappings, while some
+ * comes from single extents which overlap the range entirely. The
+ * outer loop is as follows:
+ *
+ * 1. For all reverse mappings overlapping the refcount extent,
+ * a. If a given rmap completely overlaps, mark it as seen.
+ * b. Otherwise, record the fragment for later processing.
+ *
+ * Once we've seen all the rmaps, we know that for all blocks in the
+ * refcount record we want to find $refcount owners and we've already
+ * visited $seen extents that overlap all the blocks. Therefore, we
+ * need to find ($refcount - $seen) owners for every block in the
+ * extent; call that quantity $target_nr. Proceed as follows:
+ *
+ * 2. Pull the first $target_nr fragments from the list; all of them
+ * should start at or before the start of the extent.
+ * Call this subset of fragments the working set.
+ * 3. Until there are no more unprocessed fragments,
+ * a. Find the shortest fragments in the set and remove them.
+ * b. Note the block number of the end of these fragments.
+ * c. Pull the same number of fragments from the list. All of these
+ * fragments should start at the block number recorded in the
+ * previous step.
+ * d. Put those fragments in the set.
+ * 4. Check that there are $target_nr fragments remaining in the list,
+ * and that they all end at or beyond the end of the refcount extent.
+ *
+ * If the refcount is correct, all the check conditions in the algorithm
+ * should always hold true. If not, the refcount is incorrect.
+ */
+struct xfs_scrub_refcnt_frag {
+ struct list_head list;
+ struct xfs_rmap_irec rm;
+};
+
+struct xfs_scrub_refcnt_check {
+ struct xfs_scrub_context *sc;
+ struct list_head fragments;
+
+ /* refcount extent we're examining */
+ xfs_agblock_t bno;
+ xfs_extlen_t len;
+ xfs_nlink_t refcount;
+
+ /* number of owners seen */
+ xfs_nlink_t seen;
+};
+
+/*
+ * Decide if the given rmap is large enough that we can redeem it
+ * towards refcount verification now, or if it's a fragment, in
+ * which case we'll hang onto it in the hopes that we'll later
+ * discover that we've collected exactly the correct number of
+ * fragments as the refcountbt says we should have.
+ */
+STATIC int
+xfs_scrub_refcountbt_rmap_check(
+ struct xfs_btree_cur *cur,
+ struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xfs_scrub_refcnt_check *refchk = priv;
+ struct xfs_scrub_refcnt_frag *frag;
+ xfs_agblock_t rm_last;
+ xfs_agblock_t rc_last;
+ int error = 0;
+
+ if (xfs_scrub_should_terminate(refchk->sc, &error))
+ return error;
+
+ rm_last = rec->rm_startblock + rec->rm_blockcount - 1;
+ rc_last = refchk->bno + refchk->len - 1;
+
+ /* Confirm that a single-owner refc extent is a CoW stage. */
+ if (refchk->refcount == 1 && rec->rm_owner != XFS_RMAP_OWN_COW) {
+ xfs_scrub_btree_xref_set_corrupt(refchk->sc, cur, 0);
+ return 0;
+ }
+
+ if (rec->rm_startblock <= refchk->bno && rm_last >= rc_last) {
+ /*
+ * The rmap overlaps the refcount record, so we can confirm
+ * one refcount owner seen.
+ */
+ refchk->seen++;
+ } else {
+ /*
+ * This rmap covers only part of the refcount record, so
+ * save the fragment for later processing.
+ */
+ frag = kmem_alloc(sizeof(struct xfs_scrub_refcnt_frag),
+ KM_MAYFAIL | KM_NOFS);
+ if (!frag)
+ return -ENOMEM;
+ memcpy(&frag->rm, rec, sizeof(frag->rm));
+ list_add_tail(&frag->list, &refchk->fragments);
+ }
+
+ return 0;
+}
+
+/*
+ * Given a bunch of rmap fragments, iterate through them, keeping
+ * a running tally of the refcount. If this ever deviates from
+ * what we expect (which is the refcountbt's refcount minus the
+ * number of extents that totally covered the refcountbt extent),
+ * we have a refcountbt error.
+ */
+STATIC void
+xfs_scrub_refcountbt_process_rmap_fragments(
+ struct xfs_scrub_refcnt_check *refchk)
+{
+ struct list_head worklist;
+ struct xfs_scrub_refcnt_frag *frag;
+ struct xfs_scrub_refcnt_frag *n;
+ xfs_agblock_t bno;
+ xfs_agblock_t rbno;
+ xfs_agblock_t next_rbno;
+ xfs_nlink_t nr;
+ xfs_nlink_t target_nr;
+
+ target_nr = refchk->refcount - refchk->seen;
+ if (target_nr == 0)
+ return;
+
+ /*
+ * There are (refchk->rc.rc_refcount - refchk->nr refcount)
+ * references we haven't found yet. Pull that many off the
+ * fragment list and figure out where the smallest rmap ends
+ * (and therefore the next rmap should start). All the rmaps
+ * we pull off should start at or before the beginning of the
+ * refcount record's range.
+ */
+ INIT_LIST_HEAD(&worklist);
+ rbno = NULLAGBLOCK;
+ nr = 1;
+
+ /* Find all the rmaps that start at or before the refc extent. */
+ list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+ if (frag->rm.rm_startblock > refchk->bno)
+ goto done;
+ bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+ if (rbno > bno)
+ rbno = bno;
+ list_del(&frag->list);
+ list_add_tail(&frag->list, &worklist);
+ if (nr == target_nr)
+ break;
+ nr++;
+ }
+
+ /*
+ * We should have found exactly $target_nr rmap fragments starting
+ * at or before the refcount extent.
+ */
+ if (nr != target_nr)
+ goto done;
+
+ while (!list_empty(&refchk->fragments)) {
+ /* Discard any fragments ending at rbno. */
+ nr = 0;
+ next_rbno = NULLAGBLOCK;
+ list_for_each_entry_safe(frag, n, &worklist, list) {
+ bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+ if (bno != rbno) {
+ if (next_rbno > bno)
+ next_rbno = bno;
+ continue;
+ }
+ list_del(&frag->list);
+ kmem_free(frag);
+ nr++;
+ }
+
+ /* Empty list? We're done. */
+ if (list_empty(&refchk->fragments))
+ break;
+
+ /* Try to add nr rmaps starting at rbno to the worklist. */
+ list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+ bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
+ if (frag->rm.rm_startblock != rbno)
+ goto done;
+ list_del(&frag->list);
+ list_add_tail(&frag->list, &worklist);
+ if (next_rbno > bno)
+ next_rbno = bno;
+ nr--;
+ if (nr == 0)
+ break;
+ }
+
+ rbno = next_rbno;
+ }
+
+ /*
+ * Make sure the last extent we processed ends at or beyond
+ * the end of the refcount extent.
+ */
+ if (rbno < refchk->bno + refchk->len)
+ goto done;
+
+ /* Actually record us having seen the remaining refcount. */
+ refchk->seen = refchk->refcount;
+done:
+ /* Delete fragments and work list. */
+ list_for_each_entry_safe(frag, n, &worklist, list) {
+ list_del(&frag->list);
+ kmem_free(frag);
+ }
+ list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
+ list_del(&frag->list);
+ kmem_free(frag);
+ }
+}
+
+/* Use the rmap entries covering this extent to verify the refcount. */
+STATIC void
+xfs_scrub_refcountbt_xref_rmap(
+ struct xfs_scrub_context *sc,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ xfs_nlink_t refcount)
+{
+ struct xfs_scrub_refcnt_check refchk = {
+ .sc = sc,
+ .bno = bno,
+ .len = len,
+ .refcount = refcount,
+ .seen = 0,
+ };
+ struct xfs_rmap_irec low;
+ struct xfs_rmap_irec high;
+ struct xfs_scrub_refcnt_frag *frag;
+ struct xfs_scrub_refcnt_frag *n;
+ int error;
+
+ if (!sc->sa.rmap_cur)
+ return;
+
+ /* Cross-reference with the rmapbt to confirm the refcount. */
+ memset(&low, 0, sizeof(low));
+ low.rm_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.rm_startblock = bno + len - 1;
+
+ INIT_LIST_HEAD(&refchk.fragments);
+ error = xfs_rmap_query_range(sc->sa.rmap_cur, &low, &high,
+ &xfs_scrub_refcountbt_rmap_check, &refchk);
+ if (!xfs_scrub_should_xref(sc, &error, &sc->sa.rmap_cur))
+ goto out_free;
+
+ xfs_scrub_refcountbt_process_rmap_fragments(&refchk);
+ if (refcount != refchk.seen)
+ xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+
+out_free:
+ list_for_each_entry_safe(frag, n, &refchk.fragments, list) {
+ list_del(&frag->list);
+ kmem_free(frag);
+ }
+}
+
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_refcountbt_xref(
@@ -61,6 +329,7 @@ xfs_scrub_refcountbt_xref(
xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, len);
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
+ xfs_scrub_refcountbt_xref_rmap(sc, bno, len, refcount);
}
/* Scrub a refcountbt record. */
@@ -70,6 +339,7 @@ xfs_scrub_refcountbt_rec(
union xfs_btree_rec *rec)
{
struct xfs_mount *mp = bs->cur->bc_mp;
+ xfs_agblock_t *cow_blocks = bs->private;
xfs_agnumber_t agno = bs->cur->bc_private.a.agno;
xfs_agblock_t bno;
xfs_extlen_t len;
@@ -85,6 +355,8 @@ xfs_scrub_refcountbt_rec(
has_cowflag = (bno & XFS_REFC_COW_START);
if ((refcount == 1 && !has_cowflag) || (refcount != 1 && has_cowflag))
xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
+ if (has_cowflag)
+ (*cow_blocks) += len;
/* Check the extent. */
bno &= ~XFS_REFC_COW_START;
@@ -104,14 +376,53 @@ xfs_scrub_refcountbt_rec(
return error;
}
+/* Make sure we have as many refc blocks as the rmap says. */
+STATIC void
+xfs_scrub_refcount_xref_rmap(
+ struct xfs_scrub_context *sc,
+ struct xfs_owner_info *oinfo,
+ xfs_filblks_t cow_blocks)
+{
+ xfs_extlen_t refcbt_blocks = 0;
+ xfs_filblks_t blocks;
+ int error;
+
+ /* Check that we saw as many refcbt blocks as the rmap knows about. */
+ error = xfs_btree_count_blocks(sc->sa.refc_cur, &refcbt_blocks);
+ if (!xfs_scrub_btree_process_error(sc, sc->sa.refc_cur, 0, &error))
+ return;
+ error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, oinfo,
+ &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.rmap_cur) &&
+ blocks != refcbt_blocks)
+ xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+
+ /* Check that we saw as many cow blocks as the rmap knows about. */
+ xfs_rmap_ag_owner(oinfo, XFS_RMAP_OWN_COW);
+ error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, oinfo,
+ &blocks);
+ if (xfs_scrub_should_xref(sc, &error, &sc->sa.rmap_cur) &&
+ blocks != cow_blocks)
+ xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+}
+
/* Scrub the refcount btree for some AG. */
int
xfs_scrub_refcountbt(
struct xfs_scrub_context *sc)
{
struct xfs_owner_info oinfo;
+ xfs_agblock_t cow_blocks = 0;
+ int error;
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_REFC);
- return xfs_scrub_btree(sc, sc->sa.refc_cur, xfs_scrub_refcountbt_rec,
- &oinfo, NULL);
+ error = xfs_scrub_btree(sc, sc->sa.refc_cur, xfs_scrub_refcountbt_rec,
+ &oinfo, &cow_blocks);
+ if (error)
+ return error;
+
+ if (sc->sa.rmap_cur)
+ xfs_scrub_refcount_xref_rmap(sc, &oinfo, cow_blocks);
+
+ return error;
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 13/15] xfs: cross-reference refcount btree during scrub
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (11 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 12/15] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 14/15] xfs: cross-reference the realtime bitmap Darrick J. Wong
2017-12-13 23:57 ` [PATCH 15/15] xfs: cross-reference the block mappings when possible Darrick J. Wong
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
During metadata btree scrub, we should cross-reference with the
reference counts.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
| 14 ++++++++
fs/xfs/scrub/alloc.c | 1 +
fs/xfs/scrub/bmap.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/ialloc.c | 1 +
fs/xfs/scrub/inode.c | 1 +
fs/xfs/scrub/refcount.c | 67 ++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/rmap.c | 40 ++++++++++++++++++++++++
fs/xfs/scrub/scrub.h | 6 ++++
8 files changed, 209 insertions(+)
--git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 3e5bb78..1503cbc 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -125,6 +125,7 @@ xfs_scrub_superblock_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, 1);
/* scrub teardown will take care of sc->sa for us */
}
@@ -534,6 +535,16 @@ xfs_scrub_agf_xref(
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
xfs_scrub_agf_xref_btreeblks(sc);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, 1);
+
+ /* Check agf_refcount_blocks against tree size */
+ pcur = &sc->sa.refc_cur;
+ if (*pcur) {
+ error = xfs_btree_count_blocks(*pcur, &blocks);
+ if (xfs_scrub_should_xref(sc, &error, pcur) &&
+ blocks != be32_to_cpu(agf->agf_refcount_blocks))
+ xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+ }
/* scrub teardown will take care of sc->sa for us */
}
@@ -644,6 +655,7 @@ xfs_scrub_agfl_block_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, 1);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, 1);
}
/* Scrub an AGFL block. */
@@ -703,6 +715,7 @@ xfs_scrub_agfl_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, 1);
/*
* Scrub teardown will take care of sc->sa for us. Leave sc->sa
@@ -814,6 +827,7 @@ xfs_scrub_agi_xref(
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, 1, &oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, 1);
/* scrub teardown will take care of sc->sa for us */
}
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index a4046c1..d1dd9b7 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -98,6 +98,7 @@ xfs_scrub_allocbt_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
xfs_scrub_xref_no_rmap(sc, &sc->sa.rmap_cur, bno, len);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, len);
}
/* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index f408193..2b99f06 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
#include "xfs_bmap_btree.h"
#include "xfs_rmap.h"
#include "xfs_alloc.h"
+#include "xfs_refcount.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -200,6 +201,73 @@ xfs_scrub_bmap_xref_rmap(
irec->br_startoff);
}
+/* Make sure the refcount records match this data fork extent. */
+STATIC void
+xfs_scrub_bmap_xref_refcount_data(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_scrub_ag *sa,
+ struct xfs_bmbt_irec *irec,
+ xfs_fsblock_t bno)
+{
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+ int error;
+
+ if (!sa->refc_cur)
+ return;
+
+ /* If this is shared, the inode flag must be set. */
+ error = xfs_refcount_find_shared(sa->refc_cur, bno,
+ irec->br_blockcount, &fbno, &flen, false);
+ if (!xfs_scrub_should_xref(info->sc, &error, &sa->refc_cur))
+ return;
+
+ if (flen != 0 && !xfs_is_reflink_inode(info->sc->ip))
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+}
+
+/* Make sure the refcount records match this attr fork extent. */
+STATIC void
+xfs_scrub_bmap_xref_refcount_attr(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_scrub_ag *sa,
+ struct xfs_bmbt_irec *irec,
+ xfs_fsblock_t bno)
+{
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+ int error;
+
+ if (!sa->refc_cur)
+ return;
+
+ /* No shared attr fork extents */
+ error = xfs_refcount_find_shared(sa->refc_cur, bno,
+ irec->br_blockcount, &fbno, &flen, false);
+ if (!xfs_scrub_should_xref(info->sc, &error, &sa->refc_cur))
+ return;
+
+ if (flen != 0)
+ xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+ irec->br_startoff);
+}
+
+/* Make sure the refcount records match this CoW fork extent. */
+STATIC void
+xfs_scrub_bmap_xref_refcount_cow(
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_scrub_ag *sa,
+ struct xfs_bmbt_irec *irec,
+ xfs_fsblock_t bno)
+{
+ if (!sa->refc_cur)
+ return;
+
+ xfs_scrub_xref_has_cow_staging(info->sc, &sa->refc_cur, bno,
+ irec->br_blockcount);
+}
+
/* Cross-reference a single rtdev extent record. */
STATIC void
xfs_scrub_bmap_rt_extent_xref(
@@ -238,6 +306,17 @@ xfs_scrub_bmap_extent_xref(
xfs_scrub_xref_not_inodes(info->sc, &sa.ino_cur, agbno, len);
xfs_scrub_xref_not_inodes(info->sc, &sa.fino_cur, agbno, len);
xfs_scrub_bmap_xref_rmap(info, &sa, irec, agbno);
+ switch (info->whichfork) {
+ case XFS_DATA_FORK:
+ xfs_scrub_bmap_xref_refcount_data(info, &sa, irec, agbno);
+ break;
+ case XFS_ATTR_FORK:
+ xfs_scrub_bmap_xref_refcount_attr(info, &sa, irec, agbno);
+ break;
+ case XFS_COW_FORK:
+ xfs_scrub_bmap_xref_refcount_cow(info, &sa, irec, agbno);
+ break;
+ }
xfs_scrub_ag_free(info->sc, &sa);
}
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index d4b7f13..367dd5f 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -94,6 +94,7 @@ xfs_scrub_iallocbt_chunk_xref(
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, bno, len, &oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, bno, len);
}
/* Is this chunk worth checking? */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 830d44a..6ea2c96 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -603,6 +603,7 @@ xfs_scrub_inode_xref(
xfs_scrub_xref_are_inodes(sc, &sc->sa.fino_cur, agbno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, agbno, 1, &oinfo);
+ xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, agbno, 1);
xfs_scrub_ag_free(sc, &sa);
}
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 6efae58..dd54435 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -31,6 +31,7 @@
#include "xfs_sb.h"
#include "xfs_alloc.h"
#include "xfs_rmap.h"
+#include "xfs_refcount.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -426,3 +427,69 @@ xfs_scrub_refcountbt(
return error;
}
+
+/* xref check that a cow staging extent is marked in the refcountbt. */
+void
+xfs_scrub_xref_has_cow_staging(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ struct xfs_refcount_irec rc;
+ bool has_cowflag;
+ int has_refcount;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ /* Find the CoW staging extent. */
+ error = xfs_refcount_lookup_le(*pcur, bno + XFS_REFC_COW_START,
+ &has_refcount);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ return;
+ if (!has_refcount) {
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ return;
+ }
+
+ error = xfs_refcount_get_rec(*pcur, &rc, &has_refcount);
+ if (!xfs_scrub_should_xref(sc, &error, pcur))
+ return;
+ if (!has_refcount) {
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+ return;
+ }
+
+ /* CoW flag must be set, refcount must be 1. */
+ has_cowflag = (rc.rc_startblock & XFS_REFC_COW_START);
+ if (!has_cowflag || rc.rc_refcount != 1)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+
+ /* Must be at least as long as what was passed in */
+ if (rc.rc_blockcount < len)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
+
+/*
+ * xref check that the extent is not shared. Only file data blocks
+ * can have multiple owners.
+ */
+void
+xfs_scrub_xref_not_shared(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ bool shared;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ error = xfs_refcount_has_record(*pcur, bno, len, &shared);
+ if (xfs_scrub_should_xref(sc, &error, pcur) && shared)
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 7970e73..fee31e9 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -32,6 +32,7 @@
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_rmap.h"
+#include "xfs_refcount.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -51,6 +52,43 @@ xfs_scrub_setup_ag_rmapbt(
/* Reverse-mapping scrubber. */
+/* Cross-reference a rmap against the refcount btree. */
+STATIC void
+xfs_scrub_rmapbt_xref_refc(
+ struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur,
+ struct xfs_rmap_irec *irec)
+{
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+ bool non_inode;
+ bool is_bmbt;
+ bool is_attr;
+ bool is_unwritten;
+ int error;
+
+ if (!(*pcur))
+ return;
+
+ if (irec->rm_owner == XFS_RMAP_OWN_COW) {
+ xfs_scrub_xref_has_cow_staging(sc, pcur, irec->rm_startblock,
+ irec->rm_blockcount);
+ 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(*pcur, irec->rm_startblock,
+ irec->rm_blockcount, &fbno, &flen, false);
+ if (xfs_scrub_should_xref(sc, &error, pcur) &&
+ flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten))
+ xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
+
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_rmapbt_xref(
@@ -67,6 +105,8 @@ xfs_scrub_rmapbt_xref(
xfs_scrub_xref_not_inodes(sc, &sc->sa.ino_cur, bno, len);
xfs_scrub_xref_not_inodes(sc, &sc->sa.fino_cur, bno, len);
}
+
+ xfs_scrub_rmapbt_xref_refc(sc, &sc->sa.refc_cur, irec);
}
/* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 4789dba..83cdcd8 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -142,5 +142,11 @@ void xfs_scrub_xref_not_owned_by(struct xfs_scrub_context *sc,
void xfs_scrub_xref_no_rmap(struct xfs_scrub_context *sc,
struct xfs_btree_cur **pcur, xfs_agblock_t bno,
xfs_extlen_t len);
+void xfs_scrub_xref_has_cow_staging(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
+void xfs_scrub_xref_not_shared(struct xfs_scrub_context *sc,
+ struct xfs_btree_cur **pcur, xfs_agblock_t bno,
+ xfs_extlen_t len);
#endif /* __XFS_SCRUB_SCRUB_H__ */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 14/15] xfs: cross-reference the realtime bitmap
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (12 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 13/15] xfs: cross-reference refcount btree during scrub Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
2017-12-13 23:57 ` [PATCH 15/15] xfs: cross-reference the block mappings when possible Darrick J. Wong
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
While we're scrubbing various btrees, cross-reference the records
with the other metadata.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_rtbitmap.c | 30 ++++++++++++++++++++++++++++++
fs/xfs/scrub/bmap.c | 2 ++
fs/xfs/scrub/rtbitmap.c | 20 ++++++++++++++++++++
fs/xfs/scrub/scrub.h | 6 ++++++
fs/xfs/xfs_rtalloc.h | 4 ++++
5 files changed, 62 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c
index 3fb29a5..a3f25a2 100644
--- a/fs/xfs/libxfs/xfs_rtbitmap.c
+++ b/fs/xfs/libxfs/xfs_rtbitmap.c
@@ -1097,3 +1097,33 @@ xfs_verify_rtbno(
{
return rtbno < mp->m_sb.sb_rblocks;
}
+
+/* Is the given extent all free? */
+int
+xfs_rtalloc_extent_is_free(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ xfs_rtblock_t start,
+ xfs_rtblock_t len,
+ bool *is_free)
+{
+ xfs_rtblock_t end;
+ xfs_extlen_t clen;
+ int matches;
+ int error;
+
+ *is_free = false;
+ while (len) {
+ clen = len > ~0U ? ~0U : len;
+ error = xfs_rtcheck_range(mp, tp, start, clen, 1, &end,
+ &matches);
+ if (error || !matches || end < start + clen)
+ return error;
+
+ len -= end - start;
+ start = end + 1;
+ }
+
+ *is_free = true;
+ return error;
+}
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 2b99f06..1e5ab53 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -276,6 +276,8 @@ xfs_scrub_bmap_rt_extent_xref(
struct xfs_btree_cur *cur,
struct xfs_bmbt_irec *irec)
{
+ xfs_scrub_xref_not_rtfree(info->sc, irec->br_startblock,
+ irec->br_blockcount);
}
/* Cross-reference a single datadev extent record. */
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index 6860d5d..bdf1484 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -98,3 +98,23 @@ xfs_scrub_rtsummary(
/* XXX: implement this some day */
return -ENOENT;
}
+
+
+/* xref check that the extent is not free in the rtbitmap */
+void
+xfs_scrub_xref_not_rtfree(
+ struct xfs_scrub_context *sc,
+ xfs_fsblock_t fsbno,
+ xfs_fsblock_t len)
+{
+ bool is_free;
+ int error;
+
+ xfs_ilock(sc->mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+ error = xfs_rtalloc_extent_is_free(sc->mp, sc->tp, fsbno, len,
+ &is_free);
+ if (xfs_scrub_should_xref(sc, &error, NULL) && is_free)
+ xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino,
+ NULL);
+ xfs_iunlock(sc->mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+}
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 83cdcd8..91cc094 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -148,5 +148,11 @@ void xfs_scrub_xref_has_cow_staging(struct xfs_scrub_context *sc,
void xfs_scrub_xref_not_shared(struct xfs_scrub_context *sc,
struct xfs_btree_cur **pcur, xfs_agblock_t bno,
xfs_extlen_t len);
+#ifdef CONFIG_XFS_RT
+void xfs_scrub_xref_not_rtfree(struct xfs_scrub_context *sc,
+ xfs_fsblock_t fsbno, xfs_fsblock_t len);
+#else
+# define xfs_scrub_xref_not_rtfree(sc, fsbno, len) do { } while (0)
+#endif
#endif /* __XFS_SCRUB_SCRUB_H__ */
diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h
index 3f30f84..540d506 100644
--- a/fs/xfs/xfs_rtalloc.h
+++ b/fs/xfs/xfs_rtalloc.h
@@ -139,6 +139,9 @@ int xfs_rtalloc_query_all(struct xfs_trans *tp,
xfs_rtalloc_query_range_fn fn,
void *priv);
bool xfs_verify_rtbno(struct xfs_mount *mp, xfs_rtblock_t rtbno);
+int xfs_rtalloc_extent_is_free(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_rtblock_t start, xfs_rtblock_t len,
+ bool *is_free);
#else
# define xfs_rtallocate_extent(t,b,min,max,l,f,p,rb) (ENOSYS)
# define xfs_rtfree_extent(t,b,l) (ENOSYS)
@@ -148,6 +151,7 @@ bool xfs_verify_rtbno(struct xfs_mount *mp, xfs_rtblock_t rtbno);
# define xfs_rtalloc_query_all(t,f,p) (ENOSYS)
# define xfs_rtbuf_get(m,t,b,i,p) (ENOSYS)
# define xfs_verify_rtbno(m, r) (false)
+# define xfs_rtalloc_extent_is_free(m,t,s,l,i) (ENOSYS)
static inline int /* error */
xfs_rtmount_init(
xfs_mount_t *mp) /* file system mount structure */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 15/15] xfs: cross-reference the block mappings when possible
2017-12-13 23:56 [PATCH v10.1 00/15] xfs: online scrub xref support Darrick J. Wong
` (13 preceding siblings ...)
2017-12-13 23:57 ` [PATCH 14/15] xfs: cross-reference the realtime bitmap Darrick J. Wong
@ 2017-12-13 23:57 ` Darrick J. Wong
14 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2017-12-13 23:57 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Use an inode's block mappings to cross-reference inode block counters.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/scrub/inode.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 6ea2c96..18588fb 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -37,6 +37,8 @@
#include "xfs_da_format.h"
#include "xfs_reflink.h"
#include "xfs_rmap.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_util.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -578,6 +580,37 @@ xfs_scrub_inode_map_raw(
return error;
}
+/* Cross reference the inode fields with the forks. */
+STATIC void
+xfs_scrub_inode_xref_bmap(
+ struct xfs_scrub_context *sc,
+ struct xfs_dinode *dip)
+{
+ xfs_extnum_t nextents;
+ xfs_filblks_t count;
+ xfs_filblks_t acount;
+ int error;
+
+ /* Walk all the extents to check nextents/naextents/nblocks. */
+ error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
+ &nextents, &count);
+ if (!xfs_scrub_should_xref(sc, &error, NULL))
+ return;
+ if (nextents < be32_to_cpu(dip->di_nextents))
+ xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+
+ error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
+ &nextents, &acount);
+ if (!xfs_scrub_should_xref(sc, &error, NULL))
+ return;
+ if (nextents != be16_to_cpu(dip->di_anextents))
+ xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+
+ /* Check nblocks against the inode. */
+ if (count + acount != be64_to_cpu(dip->di_nblocks))
+ xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+}
+
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_inode_xref(
@@ -604,6 +637,7 @@ xfs_scrub_inode_xref(
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
xfs_scrub_xref_owned_by(sc, &sc->sa.rmap_cur, agbno, 1, &oinfo);
xfs_scrub_xref_not_shared(sc, &sc->sa.refc_cur, agbno, 1);
+ xfs_scrub_inode_xref_bmap(sc, dip);
xfs_scrub_ag_free(sc, &sa);
}
^ permalink raw reply related [flat|nested] 16+ messages in thread