All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 00/21] xfs: online scrub xref support
@ 2017-12-23  0:42 Darrick J. Wong
  2017-12-23  0:42 ` [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl Darrick J. Wong
                   ` (20 more replies)
  0 siblings, 21 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:42 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

Hi all,

This is the eleventh revision of a patchset that adds to XFS kernel
support for online metadata scrubbing and repair.  There aren't any
on-disk format changes.  The overview of the online scrub functionality
isn't any different than it was with the first kernel series, so I'll
dive into what's in this set.

The first five patches fix some minor problems that I discovered in the
process of triaging the ~9,400 test cases (nr_objs * nr_fields *
nr_fuzz_verbs) to see if xfs_scrub identifies the corruption correctly.
These patches are totally new.

The sixteen patches after that are largely unchanged from the v10 post
except for minor bug fixes and a new patch to record cross-referencing
problems that are mistakenly identified as corruptions in 4.15.

Today's submission is the third of four parts; it adds to the various
metadata scrubbers the ability to cross-reference other metadata.  If
a given record has a corresponding record somewhere else (e.g. each
inobt record has a corresponding rmapbt record for inodes) or lacks a
corresponding record elsewhere (e.g. each bmbt record does /not/ match
to any bnobt records) then we can be far more certain of the
filesystem's correctness.  We try to augment each scrubber with as many
cross-reference checks as we possibly can, though we are careful to
avoid deadlocks by taking locks in the wrong order.  In particular, we
cannot check rmap records against inode bmbts (we lock inode and then
AG); we must only check inode bmbt records against the rmap.  This means
that the userspace driver program must be careful not to claim that the
scrub is finished unless it has scanned everything.

If you're going to start using this mess, you probably ought to just
pull from my git trees.  The kernel patches[1] should apply against
4.15-rc4.  xfsprogs[2] and xfstests[3] can be found in their usual
places.  The git trees contain all four series' worth of changes.

This is an extraordinary way to eat your data.  Enjoy!
Comments and questions are, as always, welcome.

--D

[1] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=djwong-devel
[2] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=djwong-devel
[3] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=djwong-devel

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
@ 2017-12-23  0:42 ` Darrick J. Wong
  2018-01-05  1:12   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb Darrick J. Wong
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:42 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_scrub_ag_read_headers, if we're not scrubbing the AGFL but
hit a read error reading the AGFL, we should reset the error code
so that it doesn't propagate up into the caller.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/common.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 98452ad..6ec4e10 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -302,7 +302,7 @@ xfs_scrub_ag_read_headers(
 	error = xfs_alloc_read_agfl(mp, sc->tp, agno, agfl);
 	if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGFL))
 		goto out;
-
+	error = 0;
 out:
 	return error;
 }


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
  2017-12-23  0:42 ` [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:17   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext Darrick J. Wong
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

The superblock validation routines return a variety of error codes to
reject a mount request.  For scrub we can assume that the mount
succeeded, so if we see these things appear when scrubbing secondary sb
X, we can treat them all like corruption.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index b599358..97beb47 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -126,6 +126,22 @@ xfs_scrub_superblock(
 	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
 		  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
 		  XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
+	/*
+	 * The superblock verifier can return several different error codes
+	 * if it thinks the superblock doesn't look right.  For a mount these
+	 * would all get bounced back to userspace, but if we're here then the
+	 * fs mounted successfully, which means that this secondary superblock
+	 * is simply incorrect.  Treat all these codes the same way we treat
+	 * any corruption.
+	 */
+	switch (error) {
+	case -EINVAL:	/* also -EWRONGFS */
+	case -ENOSYS:
+	case -EFBIG:
+		error = -EFSCORRUPTED;
+	default:
+		break;
+	}
 	if (!xfs_scrub_process_error(sc, agno, XFS_SB_BLOCK(mp), &error))
 		return error;
 


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
  2017-12-23  0:42 ` [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl Darrick J. Wong
  2017-12-23  0:43 ` [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:17   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 04/21] xfs: always grab transaction when scrubbing inode Darrick J. Wong
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Refactor xfs_scrub_bmap to use for_each_xfs_iext now that it exists.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/bmap.c |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)


diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 42fec0b..0261e11 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -235,7 +235,6 @@ xfs_scrub_bmap(
 	struct xfs_ifork		*ifp;
 	xfs_fileoff_t			endoff;
 	struct xfs_iext_cursor		icur;
-	bool				found;
 	int				error = 0;
 
 	ifp = XFS_IFORK_PTR(ip, whichfork);
@@ -314,9 +313,7 @@ xfs_scrub_bmap(
 	/* Scrub extent records. */
 	info.lastoff = 0;
 	ifp = XFS_IFORK_PTR(ip, whichfork);
-	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &icur, &irec);
-	     found != 0;
-	     found = xfs_iext_next_extent(ifp, &icur, &irec)) {
+	for_each_xfs_iext(ifp, &icur, &irec) {
 		if (xfs_scrub_should_terminate(sc, &error))
 			break;
 		if (isnullstartblock(irec.br_startblock))


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 04/21] xfs: always grab transaction when scrubbing inode
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (2 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:18   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode Darrick J. Wong
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Always allocate a transaction for inode scrubbing, even if the _iget
fails.  This is something that is nice to have now for consistency with
the other scrubbers but will become critical when we get to online
repair where we'll actually use the transaction + raw buffer read to fix
the verifier errors.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 2be4b25..61cd112 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -64,7 +64,7 @@ xfs_scrub_setup_inode(
 		break;
 	case -EFSCORRUPTED:
 	case -EFSBADCRC:
-		return 0;
+		return xfs_scrub_trans_alloc(sc->sm, mp, &sc->tp);
 	default:
 		return error;
 	}


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (3 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 04/21] xfs: always grab transaction when scrubbing inode Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:23   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_scrub_get_inode, we don't do a good enough job distinguishing
EINVAL returns from xfs_iget w/ IGET_UNTRUSTED -- this can happen if the
passed in inode number is invalid (past eofs, inobt says it isn't an
inode) or if the inum is actually valid but the inode buffer fails
verifier.  In the first case we still want to return ENOENT, but in the
second case we want to capture the corruption error.

Therefore, if xfs_iget returns EINVAL, try the raw imap lookup.  If that
succeeds, we conclude it's a corruption error, otherwise we just bounce
out to userspace.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/common.c |   32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)


diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 6ec4e10..d5c37d8 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -503,6 +503,7 @@ xfs_scrub_get_inode(
 	struct xfs_scrub_context	*sc,
 	struct xfs_inode		*ip_in)
 {
+	struct xfs_imap			imap;
 	struct xfs_mount		*mp = sc->mp;
 	struct xfs_inode		*ip = NULL;
 	int				error;
@@ -518,10 +519,33 @@ xfs_scrub_get_inode(
 		return -ENOENT;
 	error = xfs_iget(mp, NULL, sc->sm->sm_ino,
 			XFS_IGET_UNTRUSTED | XFS_IGET_DONTCACHE, 0, &ip);
-	if (error == -ENOENT || error == -EINVAL) {
-		/* inode doesn't exist... */
-		return -ENOENT;
-	} else if (error) {
+	switch (error) {
+	case -ENOENT:
+		/* Inode doesn't exist, just bail out. */
+		return error;
+	case 0:
+		/* Got an inode, continue. */
+		break;
+	case -EINVAL:
+		/*
+		 * -EINVAL with IGET_UNTRUSTED could mean one of several
+		 * things: userspace gave us an inode number that doesn't
+		 * correspond to fs space, or doesn't have an inobt entry;
+		 * or it could simply mean that the inode buffer failed the
+		 * read verifiers.
+		 *
+		 * Try just the inode mapping lookup -- if it succeeds, then
+		 * the inode buffer verifier failed and something needs fixing.
+		 * Otherwise, we really couldn't find it so tell userspace
+		 * that it no longer exists.
+		 */
+		error = xfs_imap(sc->mp, sc->tp, sc->sm->sm_ino, &imap,
+				XFS_IGET_UNTRUSTED | XFS_IGET_DONTCACHE);
+		if (error)
+			return -ENOENT;
+		error = -EFSCORRUPTED;
+		/* fall through */
+	default:
 		trace_xfs_scrub_op_error(sc,
 				XFS_INO_TO_AGNO(mp, sc->sm->sm_ino),
 				XFS_INO_TO_AGBNO(mp, sc->sm->sm_ino),


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (4 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:29   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 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] 94+ messages in thread

* [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (5 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:36   ` Dave Chinner
  2018-01-05 21:51   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:43 ` [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
                   ` (13 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 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 3b57ef0..5de1ef3 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2751,3 +2751,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 66a8de0..c5402bb 100644
--- a/fs/xfs/libxfs/xfs_ialloc.h
+++ b/fs/xfs/libxfs/xfs_ialloc.h
@@ -170,6 +170,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] 94+ messages in thread

* [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (6 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:40   ` Dave Chinner
  2018-01-05 21:53   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:43 ` [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
                   ` (12 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 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] 94+ messages in thread

* [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (7 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  1:41   ` Dave Chinner
  2017-12-23  0:43 ` [PATCH 10/21] xfs: set up scrub cross-referencing helpers Darrick J. Wong
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 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] 94+ messages in thread

* [PATCH 10/21] xfs: set up scrub cross-referencing helpers
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (8 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
@ 2017-12-23  0:43 ` Darrick J. Wong
  2018-01-05  2:08   ` Dave Chinner
  2018-01-05 21:54   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers Darrick J. Wong
                   ` (10 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:43 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  |   72 +++++++++++++++++++++-----
 fs/xfs/scrub/btree.h  |    9 +++
 fs/xfs/scrub/common.c |  138 +++++++++++++++++++++++++++++++++++++++++++++----
 fs/xfs/scrub/common.h |   28 ++++++++++
 fs/xfs/scrub/scrub.c  |   10 ++++
 fs/xfs/scrub/trace.h  |   22 ++++++++
 6 files changed, 257 insertions(+), 22 deletions(-)


diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index df07661..36cff8f 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,81 @@ 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_process_error_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;
+	if (xref)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+	else
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
 
 	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 +559,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 d5c37d8..9f53dfe 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_process_error_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_process_error_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.
@@ -588,3 +668,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. */
+	xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
+	*curpp = NULL;
+fail:
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+	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..d7ee72a 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,16 @@ 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);
 
+/*
+ * A libxfs function returned an error while scrubbing an object.
+ * If the function failed while operating on the object (!xref) then
+ * mark the object itself corrupt.  If the function failed while
+ * collecting cross-referencing data from other metadata (xref), then
+ * mark that the cross referencing failed.
+ */
+static inline __u32 xfs_scrub_process_error_flag(bool xref)
+{
+	return xref ? XFS_SCRUB_OFLAG_XFAIL : XFS_SCRUB_OFLAG_CORRUPT;
+}
+
 #endif	/* __XFS_SCRUB_COMMON_H__ */
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index cd46077..0ed2a12 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -110,6 +110,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] 94+ messages in thread

* [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (9 preceding siblings ...)
  2017-12-23  0:43 ` [PATCH 10/21] xfs: set up scrub cross-referencing helpers Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-05  2:11   ` Dave Chinner
  2017-12-23  0:44 ` [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

There are a few places where we make a libxfs api call on behalf of some
object other than the one we're scrubbing but inadvertently call the
regular process_error function.  When this happens we mark the object
corrupt even though it was corruption in /some other/ object that
actually produced the -EFSCORRUPTED code.  The correct output flag for
these situations is SCRUB_OFLAG_XFAIL, not SCRUB_OFLAG_CORRUPT, so fix
this now that we also have a helper to set these.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/dir.c    |    2 +-
 fs/xfs/scrub/inode.c  |    2 +-
 fs/xfs/scrub/parent.c |    6 +++---
 3 files changed, 5 insertions(+), 5 deletions(-)


diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 69e1efd..e75826b 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -92,7 +92,7 @@ xfs_scrub_dir_check_ftype(
 	 * inodes can trigger immediate inactive cleanup of the inode.
 	 */
 	error = xfs_iget(mp, sdc->sc->tp, inum, 0, 0, &ip);
-	if (!xfs_scrub_fblock_process_error(sdc->sc, XFS_DATA_FORK, offset,
+	if (!xfs_scrub_fblock_xref_process_error(sdc->sc, XFS_DATA_FORK, offset,
 			&error))
 		goto out;
 
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 61cd112..304e8bc 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -619,7 +619,7 @@ xfs_scrub_inode(
 	if (xfs_is_reflink_inode(sc->ip)) {
 		error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
 				&has_shared);
-		if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
+		if (!xfs_scrub_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
 				XFS_INO_TO_AGBNO(mp, ino), &error))
 			goto out;
 		if (!has_shared)
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 63a2533..dd704fd 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -169,7 +169,7 @@ xfs_scrub_parent_validate(
 	 * immediate inactive cleanup of the inode.
 	 */
 	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
-	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
+	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 		goto out;
 	if (dp == sc->ip) {
 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
@@ -185,7 +185,7 @@ xfs_scrub_parent_validate(
 	 */
 	if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
 		error = xfs_scrub_parent_count_parent_dentries(sc, dp, &nlink);
-		if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0,
+		if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
 				&error))
 			goto out_unlock;
 		if (nlink != expected_nlink)
@@ -205,7 +205,7 @@ xfs_scrub_parent_validate(
 
 	/* Go looking for our dentry. */
 	error = xfs_scrub_parent_count_parent_dentries(sc, dp, &nlink);
-	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
+	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 		goto out_unlock;
 
 	/* Drop the parent lock, relock this inode. */


^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (10 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-05  2:24   ` Dave Chinner
  2017-12-23  0:44 ` [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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 36cff8f..9151499 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -362,6 +362,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.
@@ -398,6 +472,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.
 	 */
@@ -468,6 +550,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;
 
@@ -559,6 +643,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] 94+ messages in thread

* [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (11 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-08 23:36   ` Dave Chinner
  2018-01-09 21:00   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 14/21] xfs: cross-reference with the bnobt Darrick J. Wong
                   ` (7 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   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(-)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 97beb47..5be9059 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.
  *
@@ -386,11 +397,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(
@@ -469,6 +492,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;
 }
@@ -481,6 +508,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(
@@ -498,6 +533,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;
 }
 
@@ -512,6 +552,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(
@@ -532,6 +579,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);
@@ -574,6 +626,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(
@@ -652,6 +711,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 0261e11..d960b7a 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 304e8bc..6cd6b34 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] 94+ messages in thread

* [PATCH 14/21] xfs: cross-reference with the bnobt
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (12 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-08 23:51   ` Dave Chinner
  2018-01-09 21:15   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 15/21] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
                   ` (6 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   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(-)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 5be9059..3bb0f96 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 */
 }
 
 /*
@@ -407,11 +421,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. */
@@ -514,6 +568,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. */
@@ -557,6 +612,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. */
@@ -631,6 +702,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 d960b7a..a74771c 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 9151499..ae58fcc 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -381,9 +381,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);
@@ -395,6 +398,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 6cd6b34..a00d179 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] 94+ messages in thread

* [PATCH 15/21] xfs: cross-reference bnobt records with cntbt
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (13 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 14/21] xfs: cross-reference with the bnobt Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-08 23:55   ` Dave Chinner
  2018-01-09 21:20   ` [PATCH v2 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 16/21] xfs: cross-reference inode btrees during scrub Darrick J. Wong
                   ` (5 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 3bb0f96..e87022f 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -444,6 +444,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);
@@ -465,6 +466,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] 94+ messages in thread

* [PATCH 16/21] xfs: cross-reference inode btrees during scrub
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (14 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 15/21] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-09 21:22   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:23   ` [PATCH v3 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 17/21] xfs: cross-reference reverse-mapping btree Darrick J. Wong
                   ` (4 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   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(+)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index e87022f..9b78218 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 */
 }
@@ -491,6 +493,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 */
 }
 
@@ -595,6 +600,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. */
@@ -649,6 +656,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
@@ -729,7 +738,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);
@@ -739,6 +752,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 a74771c..df84c58 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 a00d179..766d9ac 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] 94+ messages in thread

* [PATCH 17/21] xfs: cross-reference reverse-mapping btree
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (15 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 16/21] xfs: cross-reference inode btrees during scrub Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-09 21:24   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:25   ` [PATCH v3 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
                   ` (3 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   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   |   77 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/inode.c    |    4 ++
 fs/xfs/scrub/rmap.c     |   64 +++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h    |    9 ++++
 10 files changed, 377 insertions(+), 3 deletions(-)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 9b78218..c3d244d 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 */
 }
@@ -436,11 +440,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;
@@ -495,6 +547,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 */
 }
@@ -588,6 +643,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;
@@ -597,11 +653,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. */
@@ -624,7 +682,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;
 }
@@ -645,6 +703,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;
@@ -658,6 +717,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
@@ -705,6 +766,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;
@@ -737,6 +799,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);
@@ -765,6 +828,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 df84c58..f032aa7 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 ae58fcc..2336c84 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -407,6 +407,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 9f53dfe..1495b91c 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 d7ee72a..519f6e8 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..accec93 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,56 @@ 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);
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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 +419,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 766d9ac..8fb2aac 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] 94+ messages in thread

* [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (16 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 17/21] xfs: cross-reference reverse-mapping btree Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:26   ` [PATCH v3 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 19/21] xfs: cross-reference refcount btree during scrub Darrick J. Wong
                   ` (2 subsequent siblings)
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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 |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)


diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 8add281..b9764f5 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,56 @@ 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);
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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] 94+ messages in thread

* [PATCH 19/21] xfs: cross-reference refcount btree during scrub
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (17 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
  2017-12-23  0:44 ` [PATCH 20/21] xfs: cross-reference the realtime bitmap Darrick J. Wong
  2017-12-23  0:45 ` [PATCH 21/21] xfs: cross-reference the block mappings when possible Darrick J. Wong
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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>
---
 fs/xfs/scrub/agheader.c |   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(+)


diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index c3d244d..e36ea25 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 */
 }
@@ -550,6 +551,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 */
 }
@@ -660,6 +671,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. */
@@ -719,6 +731,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
@@ -830,6 +843,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 f032aa7..79c7767 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 accec93..8df1695 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 8fb2aac..b32a01d 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 b9764f5..afc85b4 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"
@@ -429,3 +430,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] 94+ messages in thread

* [PATCH 20/21] xfs: cross-reference the realtime bitmap
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (18 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 19/21] xfs: cross-reference refcount btree during scrub Darrick J. Wong
@ 2017-12-23  0:44 ` Darrick J. Wong
  2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
  2017-12-23  0:45 ` [PATCH 21/21] xfs: cross-reference the block mappings when possible Darrick J. Wong
  20 siblings, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:44 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 79c7767..f880c9b 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] 94+ messages in thread

* [PATCH 21/21] xfs: cross-reference the block mappings when possible
  2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
                   ` (19 preceding siblings ...)
  2017-12-23  0:44 ` [PATCH 20/21] xfs: cross-reference the realtime bitmap Darrick J. Wong
@ 2017-12-23  0:45 ` Darrick J. Wong
  2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
  20 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2017-12-23  0:45 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 b32a01d..d21866f 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] 94+ messages in thread

* Re: [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl
  2017-12-23  0:42 ` [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl Darrick J. Wong
@ 2018-01-05  1:12   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:12 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:42:56PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_scrub_ag_read_headers, if we're not scrubbing the AGFL but
> hit a read error reading the AGFL, we should reset the error code
> so that it doesn't propagate up into the caller.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Looks fine.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb
  2017-12-23  0:43 ` [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb Darrick J. Wong
@ 2018-01-05  1:17   ` Dave Chinner
  2018-01-05  1:24     ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:17 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:02PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> The superblock validation routines return a variety of error codes to
> reject a mount request.  For scrub we can assume that the mount
> succeeded, so if we see these things appear when scrubbing secondary sb
> X, we can treat them all like corruption.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> 
> diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> index b599358..97beb47 100644
> --- a/fs/xfs/scrub/agheader.c
> +++ b/fs/xfs/scrub/agheader.c
> @@ -126,6 +126,22 @@ xfs_scrub_superblock(
>  	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
>  		  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
>  		  XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
> +	/*
> +	 * The superblock verifier can return several different error codes
> +	 * if it thinks the superblock doesn't look right.  For a mount these
> +	 * would all get bounced back to userspace, but if we're here then the
> +	 * fs mounted successfully, which means that this secondary superblock
> +	 * is simply incorrect.  Treat all these codes the same way we treat
> +	 * any corruption.
> +	 */
> +	switch (error) {
> +	case -EINVAL:	/* also -EWRONGFS */
> +	case -ENOSYS:
> +	case -EFBIG:
> +		error = -EFSCORRUPTED;
> +	default:
> +		break;
> +	}
>  	if (!xfs_scrub_process_error(sc, agno, XFS_SB_BLOCK(mp), &error))
>  		return error;

Yes, this change looks fine, so

Reviewed-by: Dave Chinner <dchinner@redhat.com>

However, what I just realised is that the in-memory primary
superblock buffer that we do all our normal modification/logging
work on is an uncached buffer that accessed through xfs_getsb(), not
a cached buffer we access through xfs_trans_read_buf().

Does the scrub code take this into account?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext
  2017-12-23  0:43 ` [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext Darrick J. Wong
@ 2018-01-05  1:17   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:17 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:08PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Refactor xfs_scrub_bmap to use for_each_xfs_iext now that it exists.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/bmap.c |    5 +----
>  1 file changed, 1 insertion(+), 4 deletions(-)
> 
> 
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index 42fec0b..0261e11 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -235,7 +235,6 @@ xfs_scrub_bmap(
>  	struct xfs_ifork		*ifp;
>  	xfs_fileoff_t			endoff;
>  	struct xfs_iext_cursor		icur;
> -	bool				found;
>  	int				error = 0;
>  
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
> @@ -314,9 +313,7 @@ xfs_scrub_bmap(
>  	/* Scrub extent records. */
>  	info.lastoff = 0;
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &icur, &irec);
> -	     found != 0;
> -	     found = xfs_iext_next_extent(ifp, &icur, &irec)) {
> +	for_each_xfs_iext(ifp, &icur, &irec) {
>  		if (xfs_scrub_should_terminate(sc, &error))
>  			break;
>  		if (isnullstartblock(irec.br_startblock))

Looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 04/21] xfs: always grab transaction when scrubbing inode
  2017-12-23  0:43 ` [PATCH 04/21] xfs: always grab transaction when scrubbing inode Darrick J. Wong
@ 2018-01-05  1:18   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:18 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:14PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Always allocate a transaction for inode scrubbing, even if the _iget
> fails.  This is something that is nice to have now for consistency with
> the other scrubbers but will become critical when we get to online
> repair where we'll actually use the transaction + raw buffer read to fix
> the verifier errors.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/inode.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> 
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index 2be4b25..61cd112 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -64,7 +64,7 @@ xfs_scrub_setup_inode(
>  		break;
>  	case -EFSCORRUPTED:
>  	case -EFSBADCRC:
> -		return 0;
> +		return xfs_scrub_trans_alloc(sc->sm, mp, &sc->tp);
>  	default:
>  		return error;
>  	}

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode
  2017-12-23  0:43 ` [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode Darrick J. Wong
@ 2018-01-05  1:23   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:23 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:20PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_scrub_get_inode, we don't do a good enough job distinguishing
> EINVAL returns from xfs_iget w/ IGET_UNTRUSTED -- this can happen if the
> passed in inode number is invalid (past eofs, inobt says it isn't an
> inode) or if the inum is actually valid but the inode buffer fails
> verifier.  In the first case we still want to return ENOENT, but in the
> second case we want to capture the corruption error.
> 
> Therefore, if xfs_iget returns EINVAL, try the raw imap lookup.  If that
> succeeds, we conclude it's a corruption error, otherwise we just bounce
> out to userspace.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

Looks fine - it's in an error path so isn't going to affect anything
in the common "everything is fine" case.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb
  2018-01-05  1:17   ` Dave Chinner
@ 2018-01-05  1:24     ` Darrick J. Wong
  2018-01-05  2:10       ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05  1:24 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 12:17:14PM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:43:02PM -0800, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > The superblock validation routines return a variety of error codes to
> > reject a mount request.  For scrub we can assume that the mount
> > succeeded, so if we see these things appear when scrubbing secondary sb
> > X, we can treat them all like corruption.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
> >  1 file changed, 16 insertions(+)
> > 
> > 
> > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > index b599358..97beb47 100644
> > --- a/fs/xfs/scrub/agheader.c
> > +++ b/fs/xfs/scrub/agheader.c
> > @@ -126,6 +126,22 @@ xfs_scrub_superblock(
> >  	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
> >  		  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
> >  		  XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
> > +	/*
> > +	 * The superblock verifier can return several different error codes
> > +	 * if it thinks the superblock doesn't look right.  For a mount these
> > +	 * would all get bounced back to userspace, but if we're here then the
> > +	 * fs mounted successfully, which means that this secondary superblock
> > +	 * is simply incorrect.  Treat all these codes the same way we treat
> > +	 * any corruption.
> > +	 */
> > +	switch (error) {
> > +	case -EINVAL:	/* also -EWRONGFS */
> > +	case -ENOSYS:
> > +	case -EFBIG:
> > +		error = -EFSCORRUPTED;
> > +	default:
> > +		break;
> > +	}
> >  	if (!xfs_scrub_process_error(sc, agno, XFS_SB_BLOCK(mp), &error))
> >  		return error;
> 
> Yes, this change looks fine, so
> 
> Reviewed-by: Dave Chinner <dchinner@redhat.com>
> 
> However, what I just realised is that the in-memory primary
> superblock buffer that we do all our normal modification/logging
> work on is an uncached buffer that accessed through xfs_getsb(), not
> a cached buffer we access through xfs_trans_read_buf().
> 
> Does the scrub code take this into account?

We don't scrub the primary superblock at all, assuming that mount
will reject bad superblocks for us, and we don't touch the primary
superblock buffer at all, afaik.

--D

> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees
  2017-12-23  0:43 ` [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
@ 2018-01-05  1:29   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:29 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:29PM -0800, Darrick J. Wong wrote:
> 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>

minor nit:
> +/* 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;

Error handling logic is a bit tortured. This seems a bit clearer
to me:

	if (error == XFS_BTREE_QUERY_RANGE_ABORT) {
		*exists = true;
		return 0;
	}
	*exists = false;
	return error;

Otherwise it looks ok.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2017-12-23  0:43 ` [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
@ 2018-01-05  1:36   ` Dave Chinner
  2018-01-05  2:19     ` Darrick J. Wong
  2018-01-05 21:51   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:36 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:35PM -0800, Darrick J. Wong wrote:
> 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(+)

Some minor things.

> diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
> index 3b57ef0..5de1ef3 100644
> --- a/fs/xfs/libxfs/xfs_ialloc.c
> +++ b/fs/xfs/libxfs/xfs_ialloc.c
> @@ -2751,3 +2751,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;

has_record

> +	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;

return 0;

Then the out label can go away.

> +			}
> +		}
> +
> +		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;

Took me a minute to release that the branch was run when !error.
I'd be more obvious if it used typical error handling:

	if (error)
		return error;
	*count = ci.count;
	*freecount = ci.freecount;
	return 0;

Cheers,

Dave.

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2017-12-23  0:43 ` [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
@ 2018-01-05  1:40   ` Dave Chinner
  2018-01-05  2:49     ` Darrick J. Wong
  2018-01-05 21:53   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:40 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:41PM -0800, Darrick J. Wong wrote:
> 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;

has_record to match the other code?

> +	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);

Ok, so this returns true only if the rmap record spans the entire
range we pass in. What does it mean if the rmap record only
partially spans the range passed in?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees
  2017-12-23  0:43 ` [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
@ 2018-01-05  1:41   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  1:41 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:47PM -0800, Darrick J. Wong wrote:
> 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(+)

looks fine.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 10/21] xfs: set up scrub cross-referencing helpers
  2017-12-23  0:43 ` [PATCH 10/21] xfs: set up scrub cross-referencing helpers Darrick J. Wong
@ 2018-01-05  2:08   ` Dave Chinner
  2018-01-05  3:05     ` Darrick J. Wong
  2018-01-05 21:54   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  2:08 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:43:53PM -0800, Darrick J. Wong wrote:
> 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  |   72 +++++++++++++++++++++-----
>  fs/xfs/scrub/btree.h  |    9 +++
>  fs/xfs/scrub/common.c |  138 +++++++++++++++++++++++++++++++++++++++++++++----
>  fs/xfs/scrub/common.h |   28 ++++++++++
>  fs/xfs/scrub/scrub.c  |   10 ++++
>  fs/xfs/scrub/trace.h  |   22 ++++++++
>  6 files changed, 257 insertions(+), 22 deletions(-)

....

> -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,81 @@ 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_process_error_flag(xref);

Hmmmm.

WHy not just pass in the relevant error flag, rather than a boolean
used to choose the error flag?

>  		*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);

These then get easier to read, because there isn't a boolean that
you don't know what it means without looking at the function being
called. i.e

	return __xfs_scrub_btree_process_error(sc, cur, level, error,
						XFS_SCRUB_OFLAG_CORRUPT,
						__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);

	return __xfs_scrub_btree_process_error(sc, cur, level, error,
						XFS_SCRUB_OFLAG_XFAIL,
						__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;
> +	if (xref)
> +		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
> +	else
> +		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
>  
>  	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);
>  }

Same for these (and the other equivalent wrapper sets in the patch).

> +
> +/*
> + * 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;

Why the assert if we handle the null case just fine?

> +
> +	/* xref error, delete cursor and bail out. */
> +	xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
> +	*curpp = NULL;
> +fail:

I think the logic up to this point can be cleaned up to be:

	if (*error == 0)
		return true;

	if (curpp) {
		/* If we've already given up on xref, just bail out. */
		if (!*curpp)
			return true;

		/* xref error, delete cursor and bail out. */
		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
		*curpp = NULL;
	}


> +	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
> +	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;
> +}

.....

> @@ -139,4 +155,16 @@ 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);
>  
> +/*
> + * A libxfs function returned an error while scrubbing an object.
> + * If the function failed while operating on the object (!xref) then
> + * mark the object itself corrupt.  If the function failed while
> + * collecting cross-referencing data from other metadata (xref), then
> + * mark that the cross referencing failed.
> + */
> +static inline __u32 xfs_scrub_process_error_flag(bool xref)
> +{
> +	return xref ? XFS_SCRUB_OFLAG_XFAIL : XFS_SCRUB_OFLAG_CORRUPT;
> +}

If this is really needed, I'd like a better name - "process" doesn't
read right. Maybe  xfs_scrub_xref_fail_flag()?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb
  2018-01-05  1:24     ` Darrick J. Wong
@ 2018-01-05  2:10       ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  2:10 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Thu, Jan 04, 2018 at 05:24:10PM -0800, Darrick J. Wong wrote:
> On Fri, Jan 05, 2018 at 12:17:14PM +1100, Dave Chinner wrote:
> > On Fri, Dec 22, 2017 at 04:43:02PM -0800, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > > 
> > > The superblock validation routines return a variety of error codes to
> > > reject a mount request.  For scrub we can assume that the mount
> > > succeeded, so if we see these things appear when scrubbing secondary sb
> > > X, we can treat them all like corruption.
> > > 
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > >  fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
> > >  1 file changed, 16 insertions(+)
> > > 
> > > 
> > > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > > index b599358..97beb47 100644
> > > --- a/fs/xfs/scrub/agheader.c
> > > +++ b/fs/xfs/scrub/agheader.c
> > > @@ -126,6 +126,22 @@ xfs_scrub_superblock(
> > >  	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
> > >  		  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
> > >  		  XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_sb_buf_ops);
> > > +	/*
> > > +	 * The superblock verifier can return several different error codes
> > > +	 * if it thinks the superblock doesn't look right.  For a mount these
> > > +	 * would all get bounced back to userspace, but if we're here then the
> > > +	 * fs mounted successfully, which means that this secondary superblock
> > > +	 * is simply incorrect.  Treat all these codes the same way we treat
> > > +	 * any corruption.
> > > +	 */
> > > +	switch (error) {
> > > +	case -EINVAL:	/* also -EWRONGFS */
> > > +	case -ENOSYS:
> > > +	case -EFBIG:
> > > +		error = -EFSCORRUPTED;
> > > +	default:
> > > +		break;
> > > +	}
> > >  	if (!xfs_scrub_process_error(sc, agno, XFS_SB_BLOCK(mp), &error))
> > >  		return error;
> > 
> > Yes, this change looks fine, so
> > 
> > Reviewed-by: Dave Chinner <dchinner@redhat.com>
> > 
> > However, what I just realised is that the in-memory primary
> > superblock buffer that we do all our normal modification/logging
> > work on is an uncached buffer that accessed through xfs_getsb(), not
> > a cached buffer we access through xfs_trans_read_buf().
> > 
> > Does the scrub code take this into account?
> 
> We don't scrub the primary superblock at all, assuming that mount
> will reject bad superblocks for us, and we don't touch the primary
> superblock buffer at all, afaik.

No worries, I couldn't remember what it did here and didn't find an
obvious answer as I looked.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers
  2017-12-23  0:44 ` [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers Darrick J. Wong
@ 2018-01-05  2:11   ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  2:11 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:44:00PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> There are a few places where we make a libxfs api call on behalf of some
> object other than the one we're scrubbing but inadvertently call the
> regular process_error function.  When this happens we mark the object
> corrupt even though it was corruption in /some other/ object that
> actually produced the -EFSCORRUPTED code.  The correct output flag for
> these situations is SCRUB_OFLAG_XFAIL, not SCRUB_OFLAG_CORRUPT, so fix
> this now that we also have a helper to set these.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2018-01-05  1:36   ` Dave Chinner
@ 2018-01-05  2:19     ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05  2:19 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 12:36:06PM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:43:35PM -0800, Darrick J. Wong wrote:
> > 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(+)
> 
> Some minor things.
> 
> > diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
> > index 3b57ef0..5de1ef3 100644
> > --- a/fs/xfs/libxfs/xfs_ialloc.c
> > +++ b/fs/xfs/libxfs/xfs_ialloc.c
> > @@ -2751,3 +2751,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;
> 
> has_record

Fixed.

> > +	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;
> 
> return 0;
> 
> Then the out label can go away.

Fixed.

> > +			}
> > +		}
> > +
> > +		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;
> 
> Took me a minute to release that the branch was run when !error.
> I'd be more obvious if it used typical error handling:
> 
> 	if (error)
> 		return error;
> 	*count = ci.count;
> 	*freecount = ci.freecount;
> 	return 0;

Fixed.

--D

> Cheers,
> 
> Dave.
> 
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
  2017-12-23  0:44 ` [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
@ 2018-01-05  2:24   ` Dave Chinner
  2018-01-05  2:53     ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  2:24 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:44:06PM -0800, Darrick J. Wong wrote:
> 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 36cff8f..9151499 100644
> --- a/fs/xfs/scrub/btree.c
> +++ b/fs/xfs/scrub/btree.c
> @@ -362,6 +362,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;
> +}

I'm missing something here - where's the owner check?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2018-01-05  1:40   ` Dave Chinner
@ 2018-01-05  2:49     ` Darrick J. Wong
  2018-01-05  3:38       ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05  2:49 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 12:40:53PM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:43:41PM -0800, Darrick J. Wong wrote:
> > 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;
> 
> has_record to match the other code?

Fixed.

> > +	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);
> 
> Ok, so this returns true only if the rmap record spans the entire
> range we pass in. What does it mean if the rmap record only
> partially spans the range passed in?

For the current users (i.e. scrub) we require that the rmap completely
overlap the queried range.  If there's a gap anywhere (or mergeable
records) this function returns false and the calling scrub function
records a scrub xref failure.

How about the following change to the comment above the function?

"Is there a record for this owner completely covering a given physical
extent?  If so, *has_rmap will be set to true.  If there is no record
or the record only covers part of the range, we set *has_rmap to false.
This function doesn't perform range lookups or offset checks, so it is
not suitable for checking data fork blocks."

& maybe a:

ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) ||
       XFS_RMAP_IS_BMBT_BLOCK(offset));

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
  2018-01-05  2:24   ` Dave Chinner
@ 2018-01-05  2:53     ` Darrick J. Wong
  2018-01-05  3:39       ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05  2:53 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 01:24:36PM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:44:06PM -0800, Darrick J. Wong wrote:
> > 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 36cff8f..9151499 100644
> > --- a/fs/xfs/scrub/btree.c
> > +++ b/fs/xfs/scrub/btree.c
> > @@ -362,6 +362,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;
> > +}
> 
> I'm missing something here - where's the owner check?

"xfs: cross-reference with the bnobt" and "xfs: cross-reference with the
rmapbt" add the actual meat of checking the owner.  Both of those
patches create helpers to deal with checking the owner and recording
error statuses and uses them, so this patch only provides the shell of
the btree block checking apparatus.

Will update the commit message to say that this is only the framework.

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 10/21] xfs: set up scrub cross-referencing helpers
  2018-01-05  2:08   ` Dave Chinner
@ 2018-01-05  3:05     ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05  3:05 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 01:08:26PM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:43:53PM -0800, Darrick J. Wong wrote:
> > 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  |   72 +++++++++++++++++++++-----
> >  fs/xfs/scrub/btree.h  |    9 +++
> >  fs/xfs/scrub/common.c |  138 +++++++++++++++++++++++++++++++++++++++++++++----
> >  fs/xfs/scrub/common.h |   28 ++++++++++
> >  fs/xfs/scrub/scrub.c  |   10 ++++
> >  fs/xfs/scrub/trace.h  |   22 ++++++++
> >  6 files changed, 257 insertions(+), 22 deletions(-)
> 
> ....
> 
> > -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,81 @@ 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_process_error_flag(xref);
> 
> Hmmmm.
> 
> WHy not just pass in the relevant error flag, rather than a boolean
> used to choose the error flag?

Good idea!

> >  		*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);
> 
> These then get easier to read, because there isn't a boolean that
> you don't know what it means without looking at the function being
> called. i.e
> 
> 	return __xfs_scrub_btree_process_error(sc, cur, level, error,
> 						XFS_SCRUB_OFLAG_CORRUPT,
> 						__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);
> 
> 	return __xfs_scrub_btree_process_error(sc, cur, level, error,
> 						XFS_SCRUB_OFLAG_XFAIL,
> 						__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;
> > +	if (xref)
> > +		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
> > +	else
> > +		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
> >  
> >  	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);
> >  }
> 
> Same for these (and the other equivalent wrapper sets in the patch).

Yup.

> > +
> > +/*
> > + * 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;
> 
> Why the assert if we handle the null case just fine?
> 
> > +
> > +	/* xref error, delete cursor and bail out. */
> > +	xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
> > +	*curpp = NULL;
> > +fail:
> 
> I think the logic up to this point can be cleaned up to be:
> 
> 	if (*error == 0)
> 		return true;
> 
> 	if (curpp) {
> 		/* If we've already given up on xref, just bail out. */
> 		if (!*curpp)
> 			return true;

I think this ought to be return false, because we /had/ a cursor and then
freed it, which means there's no point in continuing with this xref.

(And yes, that's just a bug in the original code...)

> 
> 		/* xref error, delete cursor and bail out. */
> 		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
> 		*curpp = NULL;
> 	}
> 
> 
> > +	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
> > +	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;
> > +}
> 
> .....
> 
> > @@ -139,4 +155,16 @@ 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);
> >  
> > +/*
> > + * A libxfs function returned an error while scrubbing an object.
> > + * If the function failed while operating on the object (!xref) then
> > + * mark the object itself corrupt.  If the function failed while
> > + * collecting cross-referencing data from other metadata (xref), then
> > + * mark that the cross referencing failed.
> > + */
> > +static inline __u32 xfs_scrub_process_error_flag(bool xref)
> > +{
> > +	return xref ? XFS_SCRUB_OFLAG_XFAIL : XFS_SCRUB_OFLAG_CORRUPT;
> > +}
> 
> If this is really needed, I'd like a better name - "process" doesn't
> read right. Maybe  xfs_scrub_xref_fail_flag()?

It's gone. :)

Thanks for the review so far!

--D

> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2018-01-05  2:49     ` Darrick J. Wong
@ 2018-01-05  3:38       ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  3:38 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Thu, Jan 04, 2018 at 06:49:24PM -0800, Darrick J. Wong wrote:
> On Fri, Jan 05, 2018 at 12:40:53PM +1100, Dave Chinner wrote:
> > On Fri, Dec 22, 2017 at 04:43:41PM -0800, Darrick J. Wong wrote:
> > > 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;
> > 
> > has_record to match the other code?
> 
> Fixed.
> 
> > > +	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);
> > 
> > Ok, so this returns true only if the rmap record spans the entire
> > range we pass in. What does it mean if the rmap record only
> > partially spans the range passed in?
> 
> For the current users (i.e. scrub) we require that the rmap completely
> overlap the queried range.  If there's a gap anywhere (or mergeable
> records) this function returns false and the calling scrub function
> records a scrub xref failure.
> 
> How about the following change to the comment above the function?
> 
> "Is there a record for this owner completely covering a given physical
> extent?  If so, *has_rmap will be set to true.  If there is no record
> or the record only covers part of the range, we set *has_rmap to false.
> This function doesn't perform range lookups or offset checks, so it is
> not suitable for checking data fork blocks."
> 
> & maybe a:
> 
> ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) ||
>        XFS_RMAP_IS_BMBT_BLOCK(offset));

Yup, that explains what is going on a whole lot better :P

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree
  2018-01-05  2:53     ` Darrick J. Wong
@ 2018-01-05  3:39       ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-05  3:39 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Thu, Jan 04, 2018 at 06:53:20PM -0800, Darrick J. Wong wrote:
> On Fri, Jan 05, 2018 at 01:24:36PM +1100, Dave Chinner wrote:
> > On Fri, Dec 22, 2017 at 04:44:06PM -0800, Darrick J. Wong wrote:
> > > 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 36cff8f..9151499 100644
> > > --- a/fs/xfs/scrub/btree.c
> > > +++ b/fs/xfs/scrub/btree.c
> > > @@ -362,6 +362,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;
> > > +}
> > 
> > I'm missing something here - where's the owner check?
> 
> "xfs: cross-reference with the bnobt" and "xfs: cross-reference with the
> rmapbt" add the actual meat of checking the owner.  Both of those
> patches create helpers to deal with checking the owner and recording
> error statuses and uses them, so this patch only provides the shell of
> the btree block checking apparatus.
> 
> Will update the commit message to say that this is only the framework.

OK, with that you can add

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH v2 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2017-12-23  0:43 ` [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
  2018-01-05  1:36   ` Dave Chinner
@ 2018-01-05 21:51   ` Darrick J. Wong
  2018-01-16 23:05     ` Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05 21:51 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: minor fixes suggested by dchinner
---
 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 3b57ef0..decda7e 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2751,3 +2751,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_record;
+	int			i;
+	int			error;
+
+	*exists = false;
+	error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has_record);
+	while (error == 0 && has_record) {
+		error = xfs_inobt_get_rec(cur, &irec, &has_record);
+		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;
+				return 0;
+			}
+		}
+
+		error = xfs_btree_increment(cur, 0, &has_record);
+	}
+	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)
+		return error;
+
+	*count = ci.count;
+	*freecount = ci.freecount;
+	return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h
index 66a8de0..c5402bb 100644
--- a/fs/xfs/libxfs/xfs_ialloc.h
+++ b/fs/xfs/libxfs/xfs_ialloc.h
@@ -170,6 +170,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] 94+ messages in thread

* [PATCH v2 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2017-12-23  0:43 ` [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
  2018-01-05  1:40   ` Dave Chinner
@ 2018-01-05 21:53   ` Darrick J. Wong
  2018-01-06 20:46     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05 21:53 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: expand documentation to clarify the behavior of _record_exists
---
 fs/xfs/libxfs/xfs_rmap.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_rmap.h |    5 +++
 2 files changed, 72 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index 50db920..79822cf 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -2387,3 +2387,70 @@ 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 for this owner completely covering a given physical
+ * extent?  If so, *has_rmap will be set to true.  If there is no record
+ * or the record only covers part of the range, we set *has_rmap to false.
+ * This function doesn't perform range lookups or offset checks, so it is
+ * not suitable for checking data fork blocks.
+ */
+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			has_record;
+	struct xfs_rmap_irec	irec;
+	int			error;
+
+	xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+	ASSERT(XFS_RMAP_NON_INODE_OWNER(owner) ||
+	       (flags & XFS_RMAP_BMBT_BLOCK));
+
+	error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, flags,
+			&has_record);
+	if (error)
+		return error;
+	if (!has_record) {
+		*has_rmap = false;
+		return 0;
+	}
+
+	error = xfs_rmap_get_rec(cur, &irec, &has_record);
+	if (error)
+		return error;
+	if (!has_record) {
+		*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] 94+ messages in thread

* [PATCH v2 10/21] xfs: set up scrub cross-referencing helpers
  2017-12-23  0:43 ` [PATCH 10/21] xfs: set up scrub cross-referencing helpers Darrick J. Wong
  2018-01-05  2:08   ` Dave Chinner
@ 2018-01-05 21:54   ` Darrick J. Wong
  2018-01-16 23:06     ` Darrick J. Wong
  2018-01-17  0:41     ` Dave Chinner
  1 sibling, 2 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-05 21:54 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: pass corruption flags directly to helpers, reducing helper function count
---
 fs/xfs/scrub/btree.c  |   71 +++++++++++++++++++++-----
 fs/xfs/scrub/btree.h  |    9 +++
 fs/xfs/scrub/common.c |  135 +++++++++++++++++++++++++++++++++++++++++++++----
 fs/xfs/scrub/common.h |   16 ++++++
 fs/xfs/scrub/scrub.c  |   10 ++++
 fs/xfs/scrub/trace.h  |   22 ++++++++
 6 files changed, 241 insertions(+), 22 deletions(-)

diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index df07661..e17d154 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,
+	__u32				errflag,
+	void				*ret_ip)
 {
 	if (*error == 0)
 		return true;
@@ -60,36 +62,80 @@ 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 |= errflag;
 		*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,
+			XFS_SCRUB_OFLAG_CORRUPT, __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,
+			XFS_SCRUB_OFLAG_XFAIL, __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,
+	__u32				errflag,
+	void				*ret_ip)
 {
-	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	sc->sm->sm_flags |= errflag;
 
 	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, XFS_SCRUB_OFLAG_CORRUPT,
+			__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, XFS_SCRUB_OFLAG_XCORRUPT,
+			__return_address);
 }
 
 /*
@@ -512,5 +558,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 d5c37d8..35c47cf 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,
+	__u32				errflag,
+	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 |= errflag;
 		*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,
+			XFS_SCRUB_OFLAG_CORRUPT, __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,
+			XFS_SCRUB_OFLAG_XFAIL, __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,
+	__u32				errflag,
+	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 |= errflag;
 		*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,
+			XFS_SCRUB_OFLAG_CORRUPT, __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,
+			XFS_SCRUB_OFLAG_XFAIL, __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.
@@ -588,3 +668,38 @@ 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 (*error == 0)
+		return true;
+
+	if (curpp) {
+		/* If we've already given up on xref, just bail out. */
+		if (!*curpp)
+			return false;
+
+		/* xref error, delete cursor and bail out. */
+		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
+		*curpp = NULL;
+	}
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+	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..11a5bd0 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,
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index cd46077..0ed2a12 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -110,6 +110,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] 94+ messages in thread

* Re: [PATCH v2 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees
  2018-01-05 21:53   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-06 20:46     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-06 20:46 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 01:53:39PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: expand documentation to clarify the behavior of _record_exists

Looks good now.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs
  2017-12-23  0:44 ` [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
@ 2018-01-08 23:36   ` Dave Chinner
  2018-01-08 23:59     ` Darrick J. Wong
  2018-01-09 21:00   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-08 23:36 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:44:12PM -0800, Darrick J. Wong wrote:
> 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>
> ---
>  fs/xfs/scrub/agheader.c |   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(-)
> 
> 
> diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> index 97beb47..5be9059 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.
>   *
> @@ -386,11 +397,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:

I know this is just a set of stubs, but why isn't the "skip xref
because it's already marked corrupt" check inside the xref
functions?

That gets rid of all these gotos and makes the code simpler....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 14/21] xfs: cross-reference with the bnobt
  2017-12-23  0:44 ` [PATCH 14/21] xfs: cross-reference with the bnobt Darrick J. Wong
@ 2018-01-08 23:51   ` Dave Chinner
  2018-01-09  0:34     ` Darrick J. Wong
  2018-01-09 21:15   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-08 23:51 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:44:18PM -0800, Darrick J. Wong wrote:
> 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>
> ---
>  fs/xfs/scrub/agheader.c |   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(-)
> 
> 
> diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> index 5be9059..3bb0f96 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);

	agbno

> +
> +	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);

"xref_not_free". That doesn't tell me what it's actually checking
is not free, or what it's checking against. Seeing as we're checking
these all against the bnobt, wouldn't something like this be more
obvious:

	xfs_scrub_is_used_space(sc, agbno, 1);

> +	/* scrub teardown will take care of sc->sa for us */
>  }
>  
>  /*
> @@ -407,11 +421,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);

agbno. (and for all the others)

> +
> +	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);
> +	}

I have no idea what xfs_scrub_should_xref() means in this context.
We're doing a xref scrub, so why are we asking if we should be
running a xref?


> 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);
> +}

Again, "should_xref" doesn't actually tell me anything about what
this function is doing...

> @@ -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);

Why do we use an on-stack struct xfs_scrub_ag here, and not the one
embedded into the scrub context like all the other functions in this
patch?

>  }
>  
>  /* Scrub a single extent record. */
> diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
> index 9151499..ae58fcc 100644
> --- a/fs/xfs/scrub/btree.c
> +++ b/fs/xfs/scrub/btree.c
> @@ -381,9 +381,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);

agbno.

> @@ -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);

Same question here - on-stack vs embedded....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 15/21] xfs: cross-reference bnobt records with cntbt
  2017-12-23  0:44 ` [PATCH 15/21] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
@ 2018-01-08 23:55   ` Dave Chinner
  2018-01-09  0:37     ` Darrick J. Wong
  2018-01-09 21:20   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-08 23:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Dec 22, 2017 at 04:44:24PM -0800, Darrick J. Wong wrote:
> 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>
> ---
>  fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
>  fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 63 insertions(+)
> 
> 
> diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> index 3bb0f96..e87022f 100644
> --- a/fs/xfs/scrub/agheader.c
> +++ b/fs/xfs/scrub/agheader.c
> @@ -444,6 +444,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);
> @@ -465,6 +466,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))

No need to endian convert 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;
> +	}

Why is this a while loop? It'll break out at the end of the first
loop - did you forget to remove that last break statement?

> +
>  	/* 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;
> +	}

Same again - I don't see why this is a while loop.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs
  2018-01-08 23:36   ` Dave Chinner
@ 2018-01-08 23:59     ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-08 23:59 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 10:36:40AM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:44:12PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> >  fs/xfs/scrub/agheader.c |   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(-)
> > 
> > 
> > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > index 97beb47..5be9059 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.
> >   *
> > @@ -386,11 +397,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:
> 
> I know this is just a set of stubs, but why isn't the "skip xref
> because it's already marked corrupt" check inside the xref
> functions?
> 
> That gets rid of all these gotos and makes the code simpler....

Done.

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 14/21] xfs: cross-reference with the bnobt
  2018-01-08 23:51   ` Dave Chinner
@ 2018-01-09  0:34     ` Darrick J. Wong
  2018-01-09  0:57       ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09  0:34 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 10:51:25AM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:44:18PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> >  fs/xfs/scrub/agheader.c |   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(-)
> > 
> > 
> > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > index 5be9059..3bb0f96 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);
> 
> 	agbno

Fixed (and all the others).

> > +
> > +	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);
> 
> "xref_not_free". That doesn't tell me what it's actually checking
> is not free, or what it's checking against. Seeing as we're checking
> these all against the bnobt, wouldn't something like this be more
> obvious:
> 
> 	xfs_scrub_is_used_space(sc, agbno, 1);

I've been trying to keep "xref" in the name of the functions that help
us do cross-referencing, but you're right that the **pcur parameter can
drop out.  I'll ... do that. :)

> > +	/* scrub teardown will take care of sc->sa for us */
> >  }
> >  
> >  /*
> > @@ -407,11 +421,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);
> 
> agbno. (and for all the others)
> 
> > +
> > +	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);
> > +	}
> 
> I have no idea what xfs_scrub_should_xref() means in this context.
> We're doing a xref scrub, so why are we asking if we should be
> running a xref?

Given that we tried to retrieve some data from some other data
structure, the function xfs_scrub_should_xref decides if we should
actually bother with the comparison checks?   In other words, if the
xfs_alloc_query_all returned an error code then we need to delete *pcur
and we can skip the "blocks != be32..." check since it makes no sense.

How about xfs_scrub_should_check_xref?

> 
> 
> > 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);
> > +}
> 
> Again, "should_xref" doesn't actually tell me anything about what
> this function is doing...
> 
> > @@ -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);
> 
> Why do we use an on-stack struct xfs_scrub_ag here, and not the one
> embedded into the scrub context like all the other functions in this
> patch?

I think this is an archaic side effect of a previous version of scrub
when the xfs_scrub_ag wasn't embedded in the scrub context. :/

IOWs, there's no reason, I'll go fix it (and the others).

--D

> >  }
> >  
> >  /* Scrub a single extent record. */
> > diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
> > index 9151499..ae58fcc 100644
> > --- a/fs/xfs/scrub/btree.c
> > +++ b/fs/xfs/scrub/btree.c
> > @@ -381,9 +381,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);
> 
> agbno.
> 
> > @@ -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);
> 
> Same question here - on-stack vs embedded....
> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 15/21] xfs: cross-reference bnobt records with cntbt
  2018-01-08 23:55   ` Dave Chinner
@ 2018-01-09  0:37     ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09  0:37 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 10:55:25AM +1100, Dave Chinner wrote:
> On Fri, Dec 22, 2017 at 04:44:24PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> >  fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
> >  fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
> >  2 files changed, 63 insertions(+)
> > 
> > 
> > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > index 3bb0f96..e87022f 100644
> > --- a/fs/xfs/scrub/agheader.c
> > +++ b/fs/xfs/scrub/agheader.c
> > @@ -444,6 +444,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);
> > @@ -465,6 +466,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))
> 
> No need to endian convert 0.

Fixed.

> > +				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;
> > +	}
> 
> Why is this a while loop? It'll break out at the end of the first
> loop - did you forget to remove that last break statement?

Curious use of while loops to avoid having a goto dontcareaboutcntbt label.

Time to make this (and the other one) its own function....

--D

> > +
> >  	/* 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;
> > +	}
> 
> Same again - I don't see why this is a while loop.
> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH 14/21] xfs: cross-reference with the bnobt
  2018-01-09  0:34     ` Darrick J. Wong
@ 2018-01-09  0:57       ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-09  0:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Mon, Jan 08, 2018 at 04:34:41PM -0800, Darrick J. Wong wrote:
> On Tue, Jan 09, 2018 at 10:51:25AM +1100, Dave Chinner wrote:
> > On Fri, Dec 22, 2017 at 04:44:18PM -0800, Darrick J. Wong wrote:
> > > +	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);
> > > +	}
> > 
> > I have no idea what xfs_scrub_should_xref() means in this context.
> > We're doing a xref scrub, so why are we asking if we should be
> > running a xref?
> 
> Given that we tried to retrieve some data from some other data
> structure, the function xfs_scrub_should_xref decides if we should
> actually bother with the comparison checks?   In other words, if the
> xfs_alloc_query_all returned an error code then we need to delete *pcur
> and we can skip the "blocks != be32..." check since it makes no sense.
> 
> How about xfs_scrub_should_check_xref?

Yes, that makes more sense.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH v2 13/21] xfs: introduce scrubber cross-referencing stubs
  2017-12-23  0:44 ` [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
  2018-01-08 23:36   ` Dave Chinner
@ 2018-01-09 21:00   ` Darrick J. Wong
  2018-01-10  0:12     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:00 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: move the OFLAG_CORRUPT checks into the helpers to clean up the call sites
---
 fs/xfs/scrub/agheader.c |   63 ++++++++++++++++++++++++++++++++++++++++++++++-
 fs/xfs/scrub/alloc.c    |   13 ++++++++++
 fs/xfs/scrub/bmap.c     |   29 ++++++++++++++++++++++
 fs/xfs/scrub/ialloc.c   |   15 +++++++++++
 fs/xfs/scrub/inode.c    |   12 +++++++++
 fs/xfs/scrub/refcount.c |   14 ++++++++++
 fs/xfs/scrub/rmap.c     |   12 +++++++++
 7 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 97beb47..1477aad 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,16 @@ 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)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /*
  * Scrub the filesystem superblock.
  *
@@ -386,11 +399,22 @@ xfs_scrub_superblock(
 			BBTOB(bp->b_length) - sizeof(struct xfs_dsb)))
 		xfs_scrub_block_set_corrupt(sc, bp);
 
+	xfs_scrub_superblock_xref(sc, bp);
+
 	return error;
 }
 
 /* AGF */
 
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agf_xref(
+	struct xfs_scrub_context	*sc)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub the AGF. */
 int
 xfs_scrub_agf(
@@ -469,6 +493,7 @@ xfs_scrub_agf(
 	if (agfl_count != 0 && fl_count != agfl_count)
 		xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp);
 
+	xfs_scrub_agf_xref(sc);
 out:
 	return error;
 }
@@ -481,6 +506,16 @@ 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			agbno)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub an AGFL block. */
 STATIC int
 xfs_scrub_agfl_block(
@@ -498,6 +533,8 @@ xfs_scrub_agfl_block(
 	else
 		xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp);
 
+	xfs_scrub_agfl_block_xref(sc, agbno);
+
 	return 0;
 }
 
@@ -512,6 +549,15 @@ 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)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub the AGFL. */
 int
 xfs_scrub_agfl(
@@ -532,6 +578,11 @@ xfs_scrub_agfl(
 	if (!sc->sa.agf_bp)
 		return -EFSCORRUPTED;
 
+	xfs_scrub_agfl_xref(sc);
+
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		goto out;
+
 	/* Allocate buffer to ensure uniqueness of AGFL entries. */
 	agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
 	agflcount = be32_to_cpu(agf->agf_flcount);
@@ -574,6 +625,15 @@ xfs_scrub_agfl(
 
 /* AGI */
 
+/* Cross-reference with the other btrees. */
+STATIC void
+xfs_scrub_agi_xref(
+	struct xfs_scrub_context	*sc)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub the AGI. */
 int
 xfs_scrub_agi(
@@ -652,6 +712,7 @@ xfs_scrub_agi(
 	if (agi->agi_pad32 != cpu_to_be32(0))
 		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
 
+	xfs_scrub_agi_xref(sc);
 out:
 	return error;
 }
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 059663e..03ed403 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -50,6 +50,17 @@ 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			agbno,
+	xfs_extlen_t			len)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub a bnobt/cntbt record. */
 STATIC int
 xfs_scrub_allocbt_rec(
@@ -70,6 +81,8 @@ xfs_scrub_allocbt_rec(
 	    !xfs_verify_agbno(mp, agno, bno + len - 1))
 		xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
 
+	xfs_scrub_allocbt_xref(bs->sc, bno, len);
+
 	return error;
 }
 
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 0261e11..b693192 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -99,6 +99,30 @@ 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)
+{
+	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
+/* 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)
+{
+	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub a single extent record. */
 STATIC int
 xfs_scrub_bmap_extent(
@@ -158,6 +182,11 @@ xfs_scrub_bmap_extent(
 		xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
 				irec->br_startoff);
 
+	if (info->is_rt)
+		xfs_scrub_bmap_rt_extent_xref(info, ip, cur, irec);
+	else
+		xfs_scrub_bmap_extent_xref(info, ip, cur, irec);
+
 	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..9294148 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -58,6 +58,19 @@ 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			agbno,
+	xfs_extlen_t			len)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Is this chunk worth checking? */
 STATIC bool
 xfs_scrub_iallocbt_chunk(
@@ -76,6 +89,8 @@ xfs_scrub_iallocbt_chunk(
 	    !xfs_verify_agbno(mp, agno, bno + len - 1))
 		xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
 
+	xfs_scrub_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len);
+
 	return true;
 }
 
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 304e8bc..2aa12e1 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -577,6 +577,17 @@ 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)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub an inode. */
 int
 xfs_scrub_inode(
@@ -626,6 +637,7 @@ xfs_scrub_inode(
 			xfs_scrub_ino_set_preen(sc, ino, bp);
 	}
 
+	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..d202382 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -50,6 +50,18 @@ 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)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub a refcountbt record. */
 STATIC int
 xfs_scrub_refcountbt_rec(
@@ -83,6 +95,8 @@ xfs_scrub_refcountbt_rec(
 	if (refcount == 0)
 		xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
 
+	xfs_scrub_refcountbt_xref(bs->sc, bno, len, refcount);
+
 	return error;
 }
 
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 97846c4..8655948 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -51,6 +51,16 @@ 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)
+{
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return;
+}
+
 /* Scrub an rmapbt record. */
 STATIC int
 xfs_scrub_rmapbt_rec(
@@ -121,6 +131,8 @@ xfs_scrub_rmapbt_rec(
 		    irec.rm_owner > XFS_RMAP_OWN_FS)
 			xfs_scrub_btree_set_corrupt(bs->sc, bs->cur, 0);
 	}
+
+	xfs_scrub_rmapbt_xref(bs->sc, &irec);
 out:
 	return error;
 }

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 14/21] xfs: cross-reference with the bnobt
  2017-12-23  0:44 ` [PATCH 14/21] xfs: cross-reference with the bnobt Darrick J. Wong
  2018-01-08 23:51   ` Dave Chinner
@ 2018-01-09 21:15   ` Darrick J. Wong
  2018-01-10  0:15     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:15 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   87 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   19 ++++++++++
 fs/xfs/scrub/bmap.c     |   19 ++++++++++
 fs/xfs/scrub/btree.c    |    5 +++
 fs/xfs/scrub/ialloc.c   |    2 +
 fs/xfs/scrub/inode.c    |   15 ++++++++
 fs/xfs/scrub/refcount.c |    2 +
 fs/xfs/scrub/rmap.c     |    5 +++
 fs/xfs/scrub/scrub.h    |    4 ++
 9 files changed, 158 insertions(+)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 1477aad..58e544b 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -107,8 +107,23 @@ 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			agbno;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	agbno = XFS_SB_BLOCK(mp);
+
+	error = xfs_scrub_ag_init(sc, agno, &sc->sa);
+	if (!xfs_scrub_xref_process_error(sc, agno, agbno, &error))
+		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+
+	/* scrub teardown will take care of sc->sa for us */
 }
 
 /*
@@ -406,13 +421,52 @@ 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);
+	xfs_agblock_t			agbno;
+	xfs_extlen_t			blocks;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	agbno = XFS_AGF_BLOCK(mp);
+
+	error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+	if (error)
+		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+
+	/* Check agf_freeblks */
+	if (sc->sa.bno_cur) {
+		blocks = 0;
+		error = xfs_alloc_query_all(sc->sa.bno_cur,
+				xfs_scrub_agf_record_bno_lengths, &blocks);
+		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.bno_cur) &&
+		    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. */
@@ -514,6 +568,8 @@ xfs_scrub_agfl_block_xref(
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 }
 
 /* Scrub an AGFL block. */
@@ -554,8 +610,25 @@ STATIC void
 xfs_scrub_agfl_xref(
 	struct xfs_scrub_context	*sc)
 {
+	struct xfs_mount		*mp = sc->mp;
+	xfs_agblock_t			agbno;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	agbno = XFS_AGFL_BLOCK(mp);
+
+	error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+	if (error)
+		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 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. */
@@ -630,8 +703,22 @@ STATIC void
 xfs_scrub_agi_xref(
 	struct xfs_scrub_context	*sc)
 {
+	struct xfs_mount		*mp = sc->mp;
+	xfs_agblock_t			agbno;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	agbno = XFS_AGI_BLOCK(mp);
+
+	error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+	if (error)
+		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 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 03ed403..564c76b 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -113,3 +113,22 @@ xfs_scrub_cntbt(
 {
 	return xfs_scrub_allocbt(sc, XFS_BTNUM_CNT);
 }
+
+/* xref check that the extent is not free */
+void
+xfs_scrub_xref_is_used_space(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				is_freesp;
+	int				error;
+
+	if (sc->sa.bno_cur)
+		return;
+
+	error = xfs_alloc_has_record(sc->sa.bno_cur, bno, len, &is_freesp);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.bno_cur) &&
+	    is_freesp)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0);
+}
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index b693192..7e8e239 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -119,8 +119,27 @@ xfs_scrub_bmap_extent_xref(
 	struct xfs_btree_cur		*cur,
 	struct xfs_bmbt_irec		*irec)
 {
+	struct xfs_mount		*mp = info->sc->mp;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			agbno;
+	xfs_extlen_t			len;
+	int				error;
+
 	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	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, &info->sc->sa);
+	if (!xfs_scrub_fblock_process_error(info->sc, info->whichfork,
+			irec->br_startoff, &error))
+		return;
+
+	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
+
+	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
 
 /* Scrub a single extent record. */
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 3bb88d8..3dbac4c 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -378,9 +378,11 @@ xfs_scrub_btree_check_block_owner(
 	xfs_daddr_t			daddr)
 {
 	xfs_agnumber_t			agno;
+	xfs_agblock_t			agbno;
 	int				error = 0;
 
 	agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
+	agbno = 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, &bs->sc->sa);
@@ -389,6 +391,9 @@ xfs_scrub_btree_check_block_owner(
 			return error;
 	}
 
+	/* Cross-reference with the bnobt. */
+	xfs_scrub_xref_is_used_space(bs->sc, agbno, 1);
+
 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
 		xfs_scrub_ag_free(bs->sc, &bs->sc->sa);
 
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 9294148..4526894 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -69,6 +69,8 @@ xfs_scrub_iallocbt_chunk_xref(
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, len);
 }
 
 /* Is this chunk worth checking? */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 2aa12e1..be9cf19 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -584,8 +584,23 @@ xfs_scrub_inode_xref(
 	xfs_ino_t			ino,
 	struct xfs_dinode		*dip)
 {
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			agbno;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	agno = XFS_INO_TO_AGNO(sc->mp, ino);
+	agbno = XFS_INO_TO_AGBNO(sc->mp, ino);
+
+	error = xfs_scrub_ag_init(sc, agno, &sc->sa);
+	if (!xfs_scrub_xref_process_error(sc, agno, agbno, &error))
+		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+
+	xfs_scrub_ag_free(sc, &sc->sa);
 }
 
 /* Scrub an inode. */
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 4c550b3..09a04ae 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -60,6 +60,8 @@ xfs_scrub_refcountbt_xref(
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, len);
 }
 
 /* Scrub a refcountbt record. */
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 8655948..54b0eac 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -57,8 +57,13 @@ xfs_scrub_rmapbt_xref(
 	struct xfs_scrub_context	*sc,
 	struct xfs_rmap_irec		*irec)
 {
+	xfs_agblock_t			agbno = irec->rm_startblock;
+	xfs_extlen_t			len = irec->rm_blockcount;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_xref_is_used_space(sc, agbno, len);
 }
 
 /* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 2a79614..006e396 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -123,4 +123,8 @@ xfs_scrub_quota(struct xfs_scrub_context *sc)
 }
 #endif
 
+/* cross-referencing helpers */
+void xfs_scrub_xref_is_used_space(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
+
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 15/21] xfs: cross-reference bnobt records with cntbt
  2017-12-23  0:44 ` [PATCH 15/21] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
  2018-01-08 23:55   ` Dave Chinner
@ 2018-01-09 21:20   ` Darrick J. Wong
  2018-01-10  0:19     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:20 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   35 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 58e544b..3b66cd4 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -434,6 +434,38 @@ xfs_scrub_agf_record_bno_lengths(
 	return 0;
 }
 
+/* Cross reference the AGF with the cntbt (freespace by length btree) */
+STATIC void
+xfs_scrub_agf_xref_cntbt(
+	struct xfs_scrub_context	*sc,
+	struct xfs_agf			*agf)
+{
+	xfs_agblock_t			agbno;
+	xfs_extlen_t			blocks;
+	int				have;
+	int				error;
+
+	if (!sc->sa.cnt_cur)
+		return;
+
+	/* Any freespace at all? */
+	error = xfs_alloc_lookup_le(sc->sa.cnt_cur, 0, -1U, &have);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.cnt_cur))
+		return;
+	if (!have) {
+		if (agf->agf_freeblks != be32_to_cpu(0))
+			xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+		return;
+	}
+
+	/* Check agf_longest */
+	error = xfs_alloc_get_rec(sc->sa.cnt_cur, &agbno, &blocks, &have);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.cnt_cur))
+		return;
+	if (!have || blocks != be32_to_cpu(agf->agf_longest))
+		xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_agf_xref(
@@ -466,6 +498,9 @@ xfs_scrub_agf_xref(
 			xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
 	}
 
+	/* Cross-reference with the cntbt. */
+	xfs_scrub_agf_xref_cntbt(sc, agf);
+
 	/* 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 564c76b..4101560 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"
@@ -49,6 +50,48 @@ xfs_scrub_setup_ag_allocbt(
 }
 
 /* Free space btree scrubber. */
+/*
+ * Ensure there's a corresponding cntbt/bnobt record matching this
+ * bnobt/cntbt record, respectively.
+ */
+STATIC void
+xfs_scrub_allocbt_xref_other(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len)
+{
+	struct xfs_btree_cur		**pcur;
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
+	int				has_otherrec;
+	int				error;
+
+	if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
+		pcur = &sc->sa.cnt_cur;
+	else
+		pcur = &sc->sa.bno_cur;
+	if (!*pcur)
+		return;
+
+	error = xfs_alloc_lookup_le(*pcur, agbno, len, &has_otherrec);
+	if (!xfs_scrub_should_check_xref(sc, &error, pcur))
+		return;
+	if (!has_otherrec) {
+		xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+		return;
+	}
+
+	error = xfs_alloc_get_rec(*pcur, &fbno, &flen, &has_otherrec);
+	if (!xfs_scrub_should_check_xref(sc, &error, pcur))
+		return;
+	if (!has_otherrec) {
+		xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+		return;
+	}
+
+	if (fbno != agbno || flen != len)
+		xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
 
 /* Cross-reference with the other btrees. */
 STATIC void
@@ -59,6 +102,8 @@ xfs_scrub_allocbt_xref(
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_allocbt_xref_other(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 16/21] xfs: cross-reference inode btrees during scrub
  2017-12-23  0:44 ` [PATCH 16/21] xfs: cross-reference inode btrees during scrub Darrick J. Wong
@ 2018-01-09 21:22   ` Darrick J. Wong
  2018-01-15 22:17     ` Dave Chinner
  2018-01-16 23:23   ` [PATCH v3 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:22 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   19 +++++++++++++
 fs/xfs/scrub/alloc.c    |    1 +
 fs/xfs/scrub/bmap.c     |    1 +
 fs/xfs/scrub/ialloc.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/inode.c    |   49 +++++++++++++++++++++++++++++++++
 fs/xfs/scrub/refcount.c |    1 +
 fs/xfs/scrub/rmap.c     |    4 +++
 fs/xfs/scrub/scrub.h    |    4 +++
 8 files changed, 149 insertions(+)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 3b66cd4..4d4ce1f 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -122,6 +122,7 @@ xfs_scrub_superblock_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -501,6 +502,8 @@ xfs_scrub_agf_xref(
 	/* Cross-reference with the cntbt. */
 	xfs_scrub_agf_xref_cntbt(sc, agf);
 
+	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+
 	/* scrub teardown will take care of sc->sa for us */
 }
 
@@ -605,6 +608,7 @@ xfs_scrub_agfl_block_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 }
 
 /* Scrub an AGFL block. */
@@ -659,6 +663,7 @@ xfs_scrub_agfl_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -739,7 +744,10 @@ xfs_scrub_agi_xref(
 	struct xfs_scrub_context	*sc)
 {
 	struct xfs_mount		*mp = sc->mp;
+	struct xfs_agi			*agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
 	xfs_agblock_t			agbno;
+	xfs_agino_t			icount;
+	xfs_agino_t			freecount;
 	int				error;
 
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
@@ -752,6 +760,17 @@ xfs_scrub_agi_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+
+	/* Check agi_count/agi_freecount */
+	if (sc->sa.ino_cur) {
+		error = xfs_ialloc_count_inodes(sc->sa.ino_cur, &icount,
+				&freecount);
+		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
+		    (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 4101560..2a58558 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -104,6 +104,7 @@ xfs_scrub_allocbt_xref(
 		return;
 
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
+	xfs_scrub_xref_not_inodes(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 7e8e239..d2b4747 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -138,6 +138,7 @@ xfs_scrub_bmap_extent_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
+	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 4526894..34c133e 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -67,10 +67,32 @@ xfs_scrub_iallocbt_chunk_xref(
 	xfs_agblock_t			agbno,
 	xfs_extlen_t			len)
 {
+	struct xfs_btree_cur		**pcur;
+	bool				has_irec;
+	int				error;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 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_check_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? */
@@ -352,3 +374,51 @@ xfs_scrub_finobt(
 {
 	return xfs_scrub_iallocbt(sc, XFS_BTNUM_FINO);
 }
+
+/* xref check that the extent is not covered by inodes */
+void
+xfs_scrub_xref_not_inodes(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				has_inodes;
+	int				error;
+
+	if (sc->sa.ino_cur) {
+		error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
+				len, &has_inodes);
+		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
+		    has_inodes)
+			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
+	}
+
+	if (sc->sa.fino_cur) {
+		error = xfs_ialloc_has_inodes_at_extent(sc->sa.fino_cur, bno,
+				len, &has_inodes);
+		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) &&
+		    has_inodes)
+			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur,
+					0);
+	}
+}
+
+/* xref check that the extent is covered by inodes */
+void
+xfs_scrub_xref_are_inodes(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				has_inodes;
+	int				error;
+
+	if (!sc->sa.ino_cur)
+		return;
+
+	error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
+			len, &has_inodes);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
+	    !has_inodes)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
+}
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index be9cf19..b48d5fb 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -39,6 +39,7 @@
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
+#include "scrub/btree.h"
 #include "scrub/trace.h"
 
 /*
@@ -577,6 +578,53 @@ xfs_scrub_inode_map_raw(
 	return error;
 }
 
+/*
+ * Make sure the finobt doesn't think this inode is free.
+ * We don't have to check the inobt ourselves because we got the inode via
+ * IGET_UNTRUSTED, which checks the inobt for us.
+ */
+static void
+xfs_scrub_inode_xref_finobt(
+	struct xfs_scrub_context	*sc,
+	xfs_ino_t			ino)
+{
+	struct xfs_inobt_rec_incore	rec;
+	xfs_agino_t			agino;
+	int				has_record;
+	int				error;
+
+	if (!sc->sa.fino_cur)
+		return;
+
+	agino = XFS_INO_TO_AGINO(sc->mp, ino);
+
+	/*
+	 * Try to get the finobt record.  If we can't get it, then we're
+	 * in good shape.
+	 */
+	error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE,
+			&has_record);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
+	    !has_record)
+		return;
+
+	error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
+	    !has_record)
+		return;
+
+	/*
+	 * Otherwise, make sure this record either doesn't cover this inode,
+	 * or that it does but it's marked present.
+	 */
+	if (rec.ir_startino > agino ||
+	    rec.ir_startino + XFS_INODES_PER_CHUNK <= agino)
+		return;
+
+	if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_inode_xref(
@@ -599,6 +647,7 @@ xfs_scrub_inode_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_inode_xref_finobt(sc, ino);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 09a04ae..700f8f1 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -62,6 +62,7 @@ xfs_scrub_refcountbt_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
+	xfs_scrub_xref_not_inodes(sc, agbno, len);
 }
 
 /* Scrub a refcountbt record. */
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 54b0eac..02704b0 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -64,6 +64,10 @@ xfs_scrub_rmapbt_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
+	if (irec->rm_owner == XFS_RMAP_OWN_INODES)
+		xfs_scrub_xref_are_inodes(sc, agbno, len);
+	else
+		xfs_scrub_xref_not_inodes(sc, agbno, len);
 }
 
 /* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 006e396..340273f 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -126,5 +126,9 @@ xfs_scrub_quota(struct xfs_scrub_context *sc)
 /* cross-referencing helpers */
 void xfs_scrub_xref_is_used_space(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_not_inodes(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_are_inodes(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 17/21] xfs: cross-reference reverse-mapping btree
  2017-12-23  0:44 ` [PATCH 17/21] xfs: cross-reference reverse-mapping btree Darrick J. Wong
@ 2018-01-09 21:24   ` Darrick J. Wong
  2018-01-15 23:04     ` Dave Chinner
  2018-01-16 23:25   ` [PATCH v3 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:24 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   68 ++++++++++++++++++++++++++++++-
 fs/xfs/scrub/alloc.c    |    1 
 fs/xfs/scrub/bmap.c     |  104 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/btree.c    |    1 
 fs/xfs/scrub/common.c   |   47 +++++++++++++++++++++
 fs/xfs/scrub/common.h   |    4 ++
 fs/xfs/scrub/ialloc.c   |   76 ++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/inode.c    |    4 ++
 fs/xfs/scrub/rmap.c     |   63 ++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h    |    8 ++++
 10 files changed, 373 insertions(+), 3 deletions(-)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 4d4ce1f..6bc8121 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			agbno;
@@ -123,6 +125,8 @@ xfs_scrub_superblock_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -467,11 +471,57 @@ xfs_scrub_agf_xref_cntbt(
 		xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
 }
 
+/* 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_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 */
+	if (sc->sa.rmap_cur) {
+		error = xfs_btree_count_blocks(sc->sa.rmap_cur, &blocks);
+		if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+			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_check_xref(sc, &error, &sc->sa.bno_cur))
+		btreeblks += blocks - 1;
+
+	error = xfs_btree_count_blocks(sc->sa.cnt_cur, &blocks);
+	if (xfs_scrub_should_check_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);
 	xfs_agblock_t			agbno;
@@ -503,6 +553,9 @@ xfs_scrub_agf_xref(
 	xfs_scrub_agf_xref_cntbt(sc, agf);
 
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_agf_xref_btreeblks(sc);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -593,6 +646,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;
@@ -602,13 +656,15 @@ struct xfs_scrub_agfl_info {
 STATIC void
 xfs_scrub_agfl_block_xref(
 	struct xfs_scrub_context	*sc,
-	xfs_agblock_t			agbno)
+	xfs_agblock_t			agbno,
+	struct xfs_owner_info		*oinfo)
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+	xfs_scrub_xref_owned_by(sc, agbno, 1, oinfo);
 }
 
 /* Scrub an AGFL block. */
@@ -628,7 +684,7 @@ xfs_scrub_agfl_block(
 	else
 		xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp);
 
-	xfs_scrub_agfl_block_xref(sc, agbno);
+	xfs_scrub_agfl_block_xref(sc, agbno, priv);
 
 	return 0;
 }
@@ -649,6 +705,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			agbno;
 	int				error;
@@ -664,6 +721,8 @@ xfs_scrub_agfl_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -711,6 +770,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;
@@ -743,6 +803,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_agi			*agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
 	xfs_agblock_t			agbno;
@@ -772,6 +833,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, agbno, 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 2a58558..b4defa4 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
 
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
 	xfs_scrub_xref_not_inodes(sc, agbno, len);
+	xfs_scrub_xref_no_rmap(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index d2b4747..ef7b461 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -99,6 +99,109 @@ 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_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 (!info->sc->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(info->sc->sa.rmap_cur, bno,
+				owner, offset, rflags, &rmap, &has_rmap);
+		if (!xfs_scrub_should_check_xref(info->sc, &error,
+				&info->sc->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(info->sc->sa.rmap_cur, bno, 0, owner,
+				offset, rflags, &has_rmap);
+		if (!xfs_scrub_should_check_xref(info->sc, &error,
+				&info->sc->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(info->sc->sa.rmap_cur, &rmap,
+				&has_rmap);
+		if (!xfs_scrub_should_check_xref(info->sc, &error,
+				&info->sc->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(
@@ -139,6 +242,7 @@ xfs_scrub_bmap_extent_xref(
 
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
 	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
+	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 3dbac4c..1bdfa99 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -393,6 +393,7 @@ xfs_scrub_btree_check_block_owner(
 
 	/* Cross-reference with the bnobt. */
 	xfs_scrub_xref_is_used_space(bs->sc, agbno, 1);
+	xfs_scrub_xref_owned_by(bs->sc, agbno, 1, bs->oinfo);
 
 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
 		xfs_scrub_ag_free(bs->sc, &bs->sc->sa);
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 68fea09..d3aaa6a 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 84c302f..bf88a67 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 34c133e..30603b4 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			agbno,
 	xfs_extlen_t			len)
 {
+	struct xfs_owner_info		oinfo;
 	struct xfs_btree_cur		**pcur;
 	bool				has_irec;
 	int				error;
@@ -93,6 +94,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, agbno, len, &oinfo);
 }
 
 /* Is this chunk worth checking? */
@@ -228,6 +232,13 @@ xfs_scrub_iallocbt_check_freemask(
 			continue;
 		}
 
+		if (ir_holemask == 0)
+			xfs_scrub_xref_owned_by(bs->sc, agbno,
+					blks_per_cluster, &oinfo);
+		else
+			xfs_scrub_xref_not_owned_by(bs->sc, agbno,
+					blks_per_cluster, &oinfo);
+
 		/* If any part of this is a hole, skip it. */
 		if (ir_holemask)
 			continue;
@@ -266,6 +277,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 +314,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)) {
@@ -347,6 +361,56 @@ 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
+	    blocks != inobt_blocks + finobt_blocks)
+		xfs_scrub_block_set_corrupt(sc, sc->sa.agi_bp);
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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_check_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(
@@ -355,10 +419,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 b48d5fb..fc546f2 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"
@@ -632,6 +633,7 @@ xfs_scrub_inode_xref(
 	xfs_ino_t			ino,
 	struct xfs_dinode		*dip)
 {
+	struct xfs_owner_info		oinfo;
 	xfs_agnumber_t			agno;
 	xfs_agblock_t			agbno;
 	int				error;
@@ -648,6 +650,8 @@ xfs_scrub_inode_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_inode_xref_finobt(sc, ino);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
+	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 02704b0..8421c6e 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -157,3 +157,66 @@ 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo,
+	bool				fs_ok)
+{
+	bool				has_rmap;
+	int				error;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo,
+			&has_rmap);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur) &&
+			has_rmap != fs_ok)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+}
+
+/* xref check that the extent is owned by a given owner */
+void
+xfs_scrub_xref_owned_by(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo)
+{
+	xfs_scrub_xref_check_owner(sc, 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo)
+{
+	xfs_scrub_xref_check_owner(sc, 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				has_rmap;
+	int				error;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur) &&
+	    has_rmap)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+}
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 340273f..53cc526 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -130,5 +130,13 @@ void xfs_scrub_xref_not_inodes(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
 void xfs_scrub_xref_are_inodes(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_owned_by(struct xfs_scrub_context *sc,
+		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,
+		xfs_agblock_t bno, xfs_extlen_t len,
+		struct xfs_owner_info *oinfo);
+void xfs_scrub_xref_no_rmap(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2017-12-23  0:44 ` [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
@ 2018-01-09 21:25   ` Darrick J. Wong
  2018-01-15 23:49     ` Dave Chinner
  2018-01-16 23:26   ` [PATCH v3 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:25 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 700f8f1..df18e47 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_check_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(
@@ -63,6 +331,7 @@ xfs_scrub_refcountbt_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
 	xfs_scrub_xref_not_inodes(sc, agbno, len);
+	xfs_scrub_refcountbt_xref_rmap(sc, agbno, len, refcount);
 }
 
 /* Scrub a refcountbt record. */
@@ -72,6 +341,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;
@@ -87,6 +357,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;
@@ -103,14 +375,56 @@ 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
+	    blocks != refcbt_blocks)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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_check_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] 94+ messages in thread

* [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub
  2017-12-23  0:44 ` [PATCH 19/21] xfs: cross-reference refcount btree during scrub Darrick J. Wong
@ 2018-01-09 21:25   ` Darrick J. Wong
  2018-01-16  2:44     ` Dave Chinner
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:25 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   13 ++++++++
 fs/xfs/scrub/alloc.c    |    1 +
 fs/xfs/scrub/bmap.c     |   77 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/ialloc.c   |    1 +
 fs/xfs/scrub/inode.c    |    1 +
 fs/xfs/scrub/refcount.c |   65 ++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/rmap.c     |   38 +++++++++++++++++++++++
 fs/xfs/scrub/scrub.h    |    4 ++
 8 files changed, 200 insertions(+)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 6bc8121..808ef3c 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -127,6 +127,7 @@ xfs_scrub_superblock_xref(
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, 1);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -556,6 +557,15 @@ xfs_scrub_agf_xref(
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
 	xfs_scrub_agf_xref_btreeblks(sc);
+	xfs_scrub_xref_not_shared(sc, agbno, 1);
+
+	/* Check agf_refcount_blocks against tree size */
+	if (sc->sa.refc_cur) {
+		error = xfs_btree_count_blocks(sc->sa.refc_cur, &blocks);
+		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur) &&
+		    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 */
 }
@@ -665,6 +675,7 @@ xfs_scrub_agfl_block_xref(
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, 1);
 }
 
 /* Scrub an AGFL block. */
@@ -723,6 +734,7 @@ xfs_scrub_agfl_xref(
 	xfs_scrub_xref_not_inodes(sc, agbno, 1);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, 1);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -835,6 +847,7 @@ xfs_scrub_agi_xref(
 
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, 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 b4defa4..3f5168f 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -106,6 +106,7 @@ xfs_scrub_allocbt_xref(
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
 	xfs_scrub_xref_not_inodes(sc, agbno, len);
 	xfs_scrub_xref_no_rmap(sc, agbno, len);
+	xfs_scrub_xref_not_shared(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index ef7b461..5c34e025 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_rmap.h"
+#include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -202,6 +203,71 @@ 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_bmbt_irec		*irec,
+	xfs_fsblock_t			bno)
+{
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
+	int				error;
+
+	if (!info->sc->sa.refc_cur)
+		return;
+
+	/* If this is shared, the inode flag must be set. */
+	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
+			irec->br_blockcount, &fbno, &flen, false);
+	if (!xfs_scrub_should_check_xref(info->sc, &error,
+			&info->sc->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_bmbt_irec		*irec,
+	xfs_fsblock_t			bno)
+{
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
+	int				error;
+
+	if (!info->sc->sa.refc_cur)
+		return;
+
+	/* No shared attr fork extents */
+	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
+			irec->br_blockcount, &fbno, &flen, false);
+	if (!xfs_scrub_should_check_xref(info->sc, &error,
+			&info->sc->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_bmbt_irec		*irec,
+	xfs_fsblock_t			bno)
+{
+	if (!info->sc->sa.refc_cur)
+		return;
+
+	xfs_scrub_xref_has_cow_staging(info->sc, bno, irec->br_blockcount);
+}
+
 /* Cross-reference a single rtdev extent record. */
 STATIC void
 xfs_scrub_bmap_rt_extent_xref(
@@ -243,6 +309,17 @@ xfs_scrub_bmap_extent_xref(
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
 	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
 	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
+	switch (info->whichfork) {
+	case XFS_DATA_FORK:
+		xfs_scrub_bmap_xref_refcount_data(info, irec, agbno);
+		break;
+	case XFS_ATTR_FORK:
+		xfs_scrub_bmap_xref_refcount_attr(info, irec, agbno);
+		break;
+	case XFS_COW_FORK:
+		xfs_scrub_bmap_xref_refcount_cow(info, irec, agbno);
+		break;
+	}
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 30603b4..fb16314 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -97,6 +97,7 @@ xfs_scrub_iallocbt_chunk_xref(
 
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
 	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, len);
 }
 
 /* Is this chunk worth checking? */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index fc546f2..f12586c 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -652,6 +652,7 @@ xfs_scrub_inode_xref(
 	xfs_scrub_inode_xref_finobt(sc, ino);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_not_shared(sc, agbno, 1);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index df18e47..6e5b12a 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"
@@ -428,3 +429,67 @@ 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	struct xfs_refcount_irec	rc;
+	bool				has_cowflag;
+	int				has_refcount;
+	int				error;
+
+	if (!sc->sa.refc_cur)
+		return;
+
+	/* Find the CoW staging extent. */
+	error = xfs_refcount_lookup_le(sc->sa.refc_cur,
+			bno + XFS_REFC_COW_START, &has_refcount);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (!has_refcount) {
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+		return;
+	}
+
+	error = xfs_refcount_get_rec(sc->sa.refc_cur, &rc, &has_refcount);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (!has_refcount) {
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 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, sc->sa.refc_cur, 0);
+
+	/* Must be at least as long as what was passed in */
+	if (rc.rc_blockcount < len)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				shared;
+	int				error;
+
+	if (!sc->sa.refc_cur)
+		return;
+
+	error = xfs_refcount_has_record(sc->sa.refc_cur, bno, len, &shared);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur) && shared)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+}
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 8421c6e..e217f98 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,42 @@ 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_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 (!sc->sa.refc_cur)
+		return;
+
+	if (irec->rm_owner == XFS_RMAP_OWN_COW) {
+		xfs_scrub_xref_has_cow_staging(sc, 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(sc->sa.refc_cur, irec->rm_startblock,
+			irec->rm_blockcount, &fbno, &flen, false);
+	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur) &&
+	    flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten))
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_rmapbt_xref(
@@ -68,6 +105,7 @@ xfs_scrub_rmapbt_xref(
 		xfs_scrub_xref_are_inodes(sc, agbno, len);
 	else
 		xfs_scrub_xref_not_inodes(sc, agbno, len);
+	xfs_scrub_rmapbt_xref_refc(sc, irec);
 }
 
 /* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 53cc526..06c2d17 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -138,5 +138,9 @@ void xfs_scrub_xref_not_owned_by(struct xfs_scrub_context *sc,
 		struct xfs_owner_info *oinfo);
 void xfs_scrub_xref_no_rmap(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_has_cow_staging(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_not_shared(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v2 20/21] xfs: cross-reference the realtime bitmap
  2017-12-23  0:44 ` [PATCH 20/21] xfs: cross-reference the realtime bitmap Darrick J. Wong
@ 2018-01-09 21:26   ` Darrick J. Wong
  2018-01-16  2:57     ` Dave Chinner
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:26 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/libxfs/xfs_rtbitmap.c |   30 ++++++++++++++++++++++++++++++
 fs/xfs/scrub/bmap.c          |    3 +++
 fs/xfs/scrub/rtbitmap.c      |   20 ++++++++++++++++++++
 fs/xfs/scrub/scrub.h         |    6 ++++++
 fs/xfs/xfs_rtalloc.h         |    4 ++++
 5 files changed, 63 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 5c34e025..2908a1b 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -278,6 +278,9 @@ xfs_scrub_bmap_rt_extent_xref(
 {
 	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	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..1828b17 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_check_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 06c2d17..0b21f34 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -142,5 +142,11 @@ void xfs_scrub_xref_has_cow_staging(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
 void xfs_scrub_xref_not_shared(struct xfs_scrub_context *sc,
 		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] 94+ messages in thread

* [PATCH v2 21/21] xfs: cross-reference the block mappings when possible
  2017-12-23  0:45 ` [PATCH 21/21] xfs: cross-reference the block mappings when possible Darrick J. Wong
@ 2018-01-09 21:26   ` Darrick J. Wong
  2018-01-16  2:58     ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-09 21:26 UTC (permalink / raw)
  To: 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>
---
v2: streamline scrubber arguments, remove stack allocated objects
---
 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 f12586c..b2fc3ac 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"
@@ -626,6 +628,37 @@ xfs_scrub_inode_xref_finobt(
 		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
 }
 
+/* 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_check_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_check_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(
@@ -653,6 +686,7 @@ xfs_scrub_inode_xref(
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
 	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
 	xfs_scrub_xref_not_shared(sc, agbno, 1);
+	xfs_scrub_inode_xref_bmap(sc, dip);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 13/21] xfs: introduce scrubber cross-referencing stubs
  2018-01-09 21:00   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-10  0:12     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-10  0:12 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:00:12PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: move the OFLAG_CORRUPT checks into the helpers to clean up the call sites
> ---

looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 14/21] xfs: cross-reference with the bnobt
  2018-01-09 21:15   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-10  0:15     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-10  0:15 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:15:36PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---

Looks much better.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 15/21] xfs: cross-reference bnobt records with cntbt
  2018-01-09 21:20   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-10  0:19     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-10  0:19 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:20:08PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/scrub/agheader.c |   35 +++++++++++++++++++++++++++++++++++
>  fs/xfs/scrub/alloc.c    |   45 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 80 insertions(+)

A little difficult to follow ("other" meaning the "other freespace
btree" is the slightly odd bit), but otherwise OK.

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 16/21] xfs: cross-reference inode btrees during scrub
  2018-01-09 21:22   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-15 22:17     ` Dave Chinner
  2018-01-16  6:30       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-15 22:17 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:22:18PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/scrub/agheader.c |   19 +++++++++++++
>  fs/xfs/scrub/alloc.c    |    1 +
>  fs/xfs/scrub/bmap.c     |    1 +
>  fs/xfs/scrub/ialloc.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/scrub/inode.c    |   49 +++++++++++++++++++++++++++++++++
>  fs/xfs/scrub/refcount.c |    1 +
>  fs/xfs/scrub/rmap.c     |    4 +++
>  fs/xfs/scrub/scrub.h    |    4 +++
>  8 files changed, 149 insertions(+)
> 
> diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> index 3b66cd4..4d4ce1f 100644
> --- a/fs/xfs/scrub/agheader.c
> +++ b/fs/xfs/scrub/agheader.c
> @@ -122,6 +122,7 @@ xfs_scrub_superblock_xref(
>  		return;
>  
>  	xfs_scrub_xref_is_used_space(sc, agbno, 1);
> +	xfs_scrub_xref_not_inodes(sc, agbno, 1);

Seems a bit strange to have "is" in "_is_used_space" and then don't
put it in "_is_not_inodes"....

> @@ -752,6 +760,17 @@ xfs_scrub_agi_xref(
>  		return;
>  
>  	xfs_scrub_xref_is_used_space(sc, agbno, 1);
> +	xfs_scrub_xref_not_inodes(sc, agbno, 1);

Hmmm, what this actually means is "_is_not_inode_chunk", right?
Kinda obscure to have the inode index checking it's "not inodes" :P

> +	if (sc->sa.ino_cur) {
> +		error = xfs_ialloc_count_inodes(sc->sa.ino_cur, &icount,
> +				&freecount);
> +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> +		    (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);
> +	}

Reduce the indent by doing:

if (!sc->sa.ino_cur)
	return;

?

> diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> index 4526894..34c133e 100644
> --- a/fs/xfs/scrub/ialloc.c
> +++ b/fs/xfs/scrub/ialloc.c
> @@ -67,10 +67,32 @@ xfs_scrub_iallocbt_chunk_xref(
>  	xfs_agblock_t			agbno,
>  	xfs_extlen_t			len)
>  {
> +	struct xfs_btree_cur		**pcur;
> +	bool				has_irec;
> +	int				error;
> +
>  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
>  		return;
>  
>  	xfs_scrub_xref_is_used_space(sc, agbno, 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_check_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);
> +	}

Same here?

>  }
>  
>  /* Is this chunk worth checking? */
> @@ -352,3 +374,51 @@ xfs_scrub_finobt(
>  {
>  	return xfs_scrub_iallocbt(sc, XFS_BTNUM_FINO);
>  }
> +
> +/* xref check that the extent is not covered by inodes */
> +void
> +xfs_scrub_xref_not_inodes(
> +	struct xfs_scrub_context	*sc,
> +	xfs_agblock_t			bno,
> +	xfs_extlen_t			len)
> +{
> +	bool				has_inodes;
> +	int				error;
> +
> +	if (sc->sa.ino_cur) {
> +		error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
> +				len, &has_inodes);
> +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> +		    has_inodes)
> +			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
> +	}
> +
> +	if (sc->sa.fino_cur) {
> +		error = xfs_ialloc_has_inodes_at_extent(sc->sa.fino_cur, bno,
> +				len, &has_inodes);
> +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) &&
> +		    has_inodes)
> +			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur,
> +					0);
> +	}
> +}
> +
> +/* xref check that the extent is covered by inodes */
> +void
> +xfs_scrub_xref_are_inodes(
> +	struct xfs_scrub_context	*sc,
> +	xfs_agblock_t			bno,
> +	xfs_extlen_t			len)
> +{
> +	bool				has_inodes;
> +	int				error;
> +
> +	if (!sc->sa.ino_cur)
> +		return;
> +
> +	error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
> +			len, &has_inodes);
> +	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> +	    !has_inodes)
> +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
> +}

That's 3 copies of that check and error handling.

xfs_scrub_xref_inode_check(
	struct xfs_scrub_context	*sc,
	xfs_agblock_t			bno,
	xfs_extlen_t			len,
	struct xfs_btree_cursor		**icur))
{
	error = xfs_ialloc_has_inodes_at_extent(*icur, bno, len, &has_inodes);
	if (xfs_scrub_should_check_xref(sc, &error, icur) &&
	    !has_inodes)
		xfs_scrub_btree_xref_set_corrupt(sc, *icur, 0);
}

And the callers become:

	if (sc->sa.ino_cur)
		xfs_scrub_xref_inode_check(sc, bno, len, &sc->sa.ino_cur);

I find that much easier to read...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 17/21] xfs: cross-reference reverse-mapping btree
  2018-01-09 21:24   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-15 23:04     ` Dave Chinner
  2018-01-16  6:38       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-15 23:04 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:24:49PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---

.....

> diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
> index 2a58558..b4defa4 100644
> --- a/fs/xfs/scrub/alloc.c
> +++ b/fs/xfs/scrub/alloc.c
> @@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
>  
>  	xfs_scrub_allocbt_xref_other(sc, agbno, len);
>  	xfs_scrub_xref_not_inodes(sc, agbno, len);
> +	xfs_scrub_xref_no_rmap(sc, agbno, len);

Hmmm - this is actually checking the rmap considers it free space,
right? so rather than cross referencing is as "no rmap record"
(which bleeds implementation into the API), wouldn't it be better
to name this consistently with the other used/free space xref
checks?  e.g. xfs_scrub_xref_rmap_is_free_space()?

> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index d2b4747..ef7b461 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -99,6 +99,109 @@ 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_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 (!info->sc->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) {

What's this mean?

> +		error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, bno,
> +				owner, offset, rflags, &rmap, &has_rmap);
> +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> +				&info->sc->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(info->sc->sa.rmap_cur, bno, 0, owner,
> +				offset, rflags, &has_rmap);
> +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> +				&info->sc->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(info->sc->sa.rmap_cur, &rmap,
> +				&has_rmap);
> +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> +				&info->sc->sa.rmap_cur))
> +			return;
> +		if (!has_rmap) {
> +			xfs_scrub_fblock_xref_set_corrupt(info->sc,
> +					info->whichfork, irec->br_startoff);
> +			return;
> +		}
> +	}

i.e. why is one branch doing a range lookup, and the other not?
Perhaps this should also be put in a helper function....

> +
> +	/* 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;
> +	}

There are two unrelated things in the different branches. Shouldn't
this irec mod be done right at the start where we are setting up for
cow vs non-cow fork checks?

.....

> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
> index 68fea09..d3aaa6a 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(

xfs_scrub_count_rmap_ownedby_irec()

> +	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)))

Urk! that takes some parsing. Perhaps something like this?

	bool irec_attr = (rec->rm_flags & XFS_RMAP_ATTR_FORK);
	bool oinfo_attr = (sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK);

	if (rec->rm_owner != sroi->oinfo->oi_owner)
		return 0;
	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
	    irec_attr == oinfo_attr)
		(*sroi->blocks) += rec->rm_blockcount;
	return 0;

?

>  	}
> +
> +	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> +	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
>  }
>  
>  /* Is this chunk worth checking? */
> @@ -228,6 +232,13 @@ xfs_scrub_iallocbt_check_freemask(
>  			continue;
>  		}
>  
> +		if (ir_holemask == 0)
> +			xfs_scrub_xref_owned_by(bs->sc, agbno,
> +					blks_per_cluster, &oinfo);
> +		else
> +			xfs_scrub_xref_not_owned_by(bs->sc, agbno,
> +					blks_per_cluster, &oinfo);
> +
>  		/* If any part of this is a hole, skip it. */
>  		if (ir_holemask)
>  			continue;

I think these two conditions should be combined, along with an
update to the comment about holes not containing inode chunks?

> @@ -266,6 +277,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 +314,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);

Line of whitespace to separate the corruption checks from the
accounting logic?

>  
>  	/* Handle non-sparse inodes */
>  	if (!xfs_inobt_issparse(irec.ir_holemask)) {
> @@ -347,6 +361,56 @@ 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;

Shouldn't a failure set some kind of corruption flag here rather
than silently failing?

> +
> +	if (xfs_sb_version_hasfinobt(&sc->mp->m_sb)) {
> +		if (!sc->sa.fino_cur)
> +			return;

Put this check at the start with the other cursor checks.

.....
> @@ -355,10 +419,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);

Why do we only xref the inobt?

.....
> diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
> index 02704b0..8421c6e 100644
> --- a/fs/xfs/scrub/rmap.c
> +++ b/fs/xfs/scrub/rmap.c
> @@ -157,3 +157,66 @@ 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,
> +	xfs_agblock_t			bno,
> +	xfs_extlen_t			len,
> +	struct xfs_owner_info		*oinfo,
> +	bool				fs_ok)

Not sure about this variable name. "record_should_exist"?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-15 23:49     ` Dave Chinner
  2018-01-16  6:49       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-15 23:49 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:25:17PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 316 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> index 700f8f1..df18e47 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.

This needs a comment somewhere in here describing the order of
the records on the fragment list. AFAICT, it's ordered by start
bno, but I'm not 100% sure and it seems the code is dependent on
strict ordering of records the frag list....

.....

> +	} 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);

I'm making the assumption here that we're seeing records in the
order they are in the rmap tree and that it's in increase startblock
order, hence the list is ordered that way....

> +	}
> +
> +	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;

.... and is where the code implies lowest to highest startblock
ordering on the frag list.

> +		bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> +		if (rbno > bno)
> +			rbno = bno;

Can we put that check the other way around? we're looking for the
shortest/smallest end block, so

		if (bno < rbno)
			rbno = bno;

Makes a lot more sense to me.

> +		list_del(&frag->list);
> +		list_add_tail(&frag->list, &worklist);

list_move_tail()?

Ok, so we are moving fragments that start before the recount bno to
the work list.

> +		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;

ok. so on error we clean up and free the frag list and work list....

> +
> +	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) {

Ok, this needs to be clearer than it's walking the working set
of fragments. I had to read this code several times before I worked
out this is where the "working set" was being processed....

> +			bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> +			if (bno != rbno) {
> +				if (next_rbno > bno)
> +					next_rbno = bno;

Same comment here about being a next_rbno "smallest bno" variable.

> +				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;

definitely assuming the frag list is ordered here :P

> +			list_del(&frag->list);
> +			list_add_tail(&frag->list, &worklist);
> +			if (next_rbno > bno)
> +				next_rbno = bno;
> +			nr--;
> +			if (nr == 0)
> +				break;
> +		}

Ok, so if we get here with nr > 0, then we must have emptied the
fragment list onto the work list, right? At this point, the outer
loop will terminate. Don't we need to run the worklist processing
loop one last time?

> +		rbno = next_rbno;
> +	}

.....
> +/* 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;

This range query init feels like a familiar pattern now. Helper
function (separate patch)?

....

> +/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> +	    blocks != refcbt_blocks)
> +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
> +
> +	if (!sc->sa.rmap_cur)
> +		return;
> +
> +	/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> +	    blocks != cow_blocks)
> +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);

Bit of a landmine that this code changes the owner info structure
that was passed in....

> +}
> +
>  /* 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);

.... because that's not obvious in this code here if we add anymore
code after this call.

> +
> +	return error;

error is zero here, so "return 0" instead?

Cheers,

Dave.

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16  2:44     ` Dave Chinner
  2018-01-16  6:52       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-16  2:44 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:25:47PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---

....

> +/* 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_bmbt_irec		*irec,
> +	xfs_fsblock_t			bno)

What's in the irec if it's not the block number of the extent we
are looking up?

Actually, I may have just answered that myself: bno is actually
an agbno, not an fsbno. So that should be:

	xfs_agblock_t			agbno)

> +{
> +	xfs_agblock_t			fbno;
> +	xfs_extlen_t			flen;
> +	int				error;
> +
> +	if (!info->sc->sa.refc_cur)
> +		return;
> +
> +	/* If this is shared, the inode flag must be set. */
> +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> +			irec->br_blockcount, &fbno, &flen, false);
> +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> +			&info->sc->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);

So is the block corrupt or is the inode flags corrupt? What
determines the object we mark as corrupt/needing fixing here?

> +/* 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_bmbt_irec		*irec,
> +	xfs_fsblock_t			bno)

agbno

> +{
> +	xfs_agblock_t			fbno;
> +	xfs_extlen_t			flen;
> +	int				error;
> +
> +	if (!info->sc->sa.refc_cur)
> +		return;
> +
> +	/* No shared attr fork extents */
> +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> +			irec->br_blockcount, &fbno, &flen, false);
> +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> +			&info->sc->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_bmbt_irec		*irec,
> +	xfs_fsblock_t			bno)

agbno

> +{
> +	if (!info->sc->sa.refc_cur)
> +		return;
> +
> +	xfs_scrub_xref_has_cow_staging(info->sc, bno, irec->br_blockcount);
> +}
> +
>  /* Cross-reference a single rtdev extent record. */
>  STATIC void
>  xfs_scrub_bmap_rt_extent_xref(
> @@ -243,6 +309,17 @@ xfs_scrub_bmap_extent_xref(
>  	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
>  	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
>  	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
> +	switch (info->whichfork) {
> +	case XFS_DATA_FORK:
> +		xfs_scrub_bmap_xref_refcount_data(info, irec, agbno);
> +		break;
> +	case XFS_ATTR_FORK:
> +		xfs_scrub_bmap_xref_refcount_attr(info, irec, agbno);
> +		break;
> +	case XFS_COW_FORK:
> +		xfs_scrub_bmap_xref_refcount_cow(info, irec, agbno);
> +		break;

This is where the agbno comes from :P

> +	}
>  
>  	xfs_scrub_ag_free(info->sc, &info->sc->sa);
>  }
> diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> index 30603b4..fb16314 100644
> --- a/fs/xfs/scrub/ialloc.c
> +++ b/fs/xfs/scrub/ialloc.c
> @@ -97,6 +97,7 @@ xfs_scrub_iallocbt_chunk_xref(
>  
>  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
>  	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
> +	xfs_scrub_xref_not_shared(sc, agbno, len);
>  }
>  
>  /* Is this chunk worth checking? */
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index fc546f2..f12586c 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -652,6 +652,7 @@ xfs_scrub_inode_xref(
>  	xfs_scrub_inode_xref_finobt(sc, ino);
>  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
>  	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
> +	xfs_scrub_xref_not_shared(sc, agbno, 1);
>  
>  	xfs_scrub_ag_free(sc, &sc->sa);
>  }
> diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> index df18e47..6e5b12a 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"
> @@ -428,3 +429,67 @@ 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,
> +	xfs_agblock_t			bno,

agbno

> +	xfs_extlen_t			len)
> +{
> +	struct xfs_refcount_irec	rc;
> +	bool				has_cowflag;
> +	int				has_refcount;
> +	int				error;
.....
> +/*
> + * 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,
> +	xfs_agblock_t			bno,

agbno

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 20/21] xfs: cross-reference the realtime bitmap
  2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16  2:57     ` Dave Chinner
  2018-01-16  6:55       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-16  2:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:26:09PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/libxfs/xfs_rtbitmap.c |   30 ++++++++++++++++++++++++++++++
>  fs/xfs/scrub/bmap.c          |    3 +++
>  fs/xfs/scrub/rtbitmap.c      |   20 ++++++++++++++++++++
>  fs/xfs/scrub/scrub.h         |    6 ++++++
>  fs/xfs/xfs_rtalloc.h         |    4 ++++
>  5 files changed, 63 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,

shouldn't that be a xfs_extlen_t?

> +	bool				*is_free)
> +{
> +	xfs_rtblock_t			end;
> +	xfs_extlen_t			clen;

clen? Not sure what that is shorthand for.


> +	int				matches;
> +	int				error;
> +
> +	*is_free = false;
> +	while (len) {
> +		clen = len > ~0U ? ~0U : len;

So we're checking for a len > UINT_MAX and clamping it here because
the rt code uses 32 bit extent lengths internally? If we actually
need 64 bit extent lengths, then isn't using UINT_MAX more obvious
as to the purpose? i.e

	if (len > UINT_MAX)
		clen = UINT_MAX;
	else
		clen = 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 5c34e025..2908a1b 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -278,6 +278,9 @@ xfs_scrub_bmap_rt_extent_xref(
>  {
>  	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
>  		return;
> +
> +	xfs_scrub_xref_not_rtfree(info->sc, irec->br_startblock,
> +			irec->br_blockcount);

So, irec->br_blockcount is a xfs_extlen, which is 32 bits....

>  }
>  
>  /* Cross-reference a single datadev extent record. */
> diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
> index 6860d5d..1828b17 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)

So we're quietly converting it to a 64 bit length here. ANd not even
using xfs_rtblock_t as you'd expect for a realtime block...

What caller needs/uses a 64 bit extent length? All the rt allocation
code uses xfs_extlen_t for lengths, so I'm a bit confused as to why
it's needed here.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 21/21] xfs: cross-reference the block mappings when possible
  2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16  2:58     ` Dave Chinner
  2018-01-16  6:55       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-16  2:58 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 09, 2018 at 01:26:33PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)

Looks fine.

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 16/21] xfs: cross-reference inode btrees during scrub
  2018-01-15 22:17     ` Dave Chinner
@ 2018-01-16  6:30       ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:30 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 09:17:11AM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:22:18PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> >  fs/xfs/scrub/agheader.c |   19 +++++++++++++
> >  fs/xfs/scrub/alloc.c    |    1 +
> >  fs/xfs/scrub/bmap.c     |    1 +
> >  fs/xfs/scrub/ialloc.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/xfs/scrub/inode.c    |   49 +++++++++++++++++++++++++++++++++
> >  fs/xfs/scrub/refcount.c |    1 +
> >  fs/xfs/scrub/rmap.c     |    4 +++
> >  fs/xfs/scrub/scrub.h    |    4 +++
> >  8 files changed, 149 insertions(+)
> > 
> > diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
> > index 3b66cd4..4d4ce1f 100644
> > --- a/fs/xfs/scrub/agheader.c
> > +++ b/fs/xfs/scrub/agheader.c
> > @@ -122,6 +122,7 @@ xfs_scrub_superblock_xref(
> >  		return;
> >  
> >  	xfs_scrub_xref_is_used_space(sc, agbno, 1);
> > +	xfs_scrub_xref_not_inodes(sc, agbno, 1);
> 
> Seems a bit strange to have "is" in "_is_used_space" and then don't
> put it in "_is_not_inodes"....

It does now. :)

> > @@ -752,6 +760,17 @@ xfs_scrub_agi_xref(
> >  		return;
> >  
> >  	xfs_scrub_xref_is_used_space(sc, agbno, 1);
> > +	xfs_scrub_xref_not_inodes(sc, agbno, 1);
> 
> Hmmm, what this actually means is "_is_not_inode_chunk", right?
> Kinda obscure to have the inode index checking it's "not inodes" :P

<nod>

> > +	if (sc->sa.ino_cur) {
> > +		error = xfs_ialloc_count_inodes(sc->sa.ino_cur, &icount,
> > +				&freecount);
> > +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> > +		    (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);
> > +	}
> 
> Reduce the indent by doing:
> 
> if (!sc->sa.ino_cur)
> 	return;
> 
> ?

The downside is that later patches add more stuff after that, and we
don't necessarily want to abort the other xref checks just because we
failed to get/bombed out of getting the ino_cur.

<shrug> OTOH I guess that hunk could just be a(nother) static inline
function, which would make the _xref functions less messy.

> > diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> > index 4526894..34c133e 100644
> > --- a/fs/xfs/scrub/ialloc.c
> > +++ b/fs/xfs/scrub/ialloc.c
> > @@ -67,10 +67,32 @@ xfs_scrub_iallocbt_chunk_xref(
> >  	xfs_agblock_t			agbno,
> >  	xfs_extlen_t			len)
> >  {
> > +	struct xfs_btree_cur		**pcur;
> > +	bool				has_irec;
> > +	int				error;
> > +
> >  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> >  		return;
> >  
> >  	xfs_scrub_xref_is_used_space(sc, agbno, 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_check_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);
> > +	}
> 
> Same here?

<nod>

> >  }
> >  
> >  /* Is this chunk worth checking? */
> > @@ -352,3 +374,51 @@ xfs_scrub_finobt(
> >  {
> >  	return xfs_scrub_iallocbt(sc, XFS_BTNUM_FINO);
> >  }
> > +
> > +/* xref check that the extent is not covered by inodes */
> > +void
> > +xfs_scrub_xref_not_inodes(
> > +	struct xfs_scrub_context	*sc,
> > +	xfs_agblock_t			bno,
> > +	xfs_extlen_t			len)
> > +{
> > +	bool				has_inodes;
> > +	int				error;
> > +
> > +	if (sc->sa.ino_cur) {
> > +		error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
> > +				len, &has_inodes);
> > +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> > +		    has_inodes)
> > +			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
> > +	}
> > +
> > +	if (sc->sa.fino_cur) {
> > +		error = xfs_ialloc_has_inodes_at_extent(sc->sa.fino_cur, bno,
> > +				len, &has_inodes);
> > +		if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) &&
> > +		    has_inodes)
> > +			xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur,
> > +					0);
> > +	}
> > +}
> > +
> > +/* xref check that the extent is covered by inodes */
> > +void
> > +xfs_scrub_xref_are_inodes(
> > +	struct xfs_scrub_context	*sc,
> > +	xfs_agblock_t			bno,
> > +	xfs_extlen_t			len)
> > +{
> > +	bool				has_inodes;
> > +	int				error;
> > +
> > +	if (!sc->sa.ino_cur)
> > +		return;
> > +
> > +	error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur, bno,
> > +			len, &has_inodes);
> > +	if (xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur) &&
> > +	    !has_inodes)
> > +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.ino_cur, 0);
> > +}
> 
> That's 3 copies of that check and error handling.
> 
> xfs_scrub_xref_inode_check(
> 	struct xfs_scrub_context	*sc,
> 	xfs_agblock_t			bno,
> 	xfs_extlen_t			len,
> 	struct xfs_btree_cursor		**icur))
> {
> 	error = xfs_ialloc_has_inodes_at_extent(*icur, bno, len, &has_inodes);
> 	if (xfs_scrub_should_check_xref(sc, &error, icur) &&
> 	    !has_inodes)
> 		xfs_scrub_btree_xref_set_corrupt(sc, *icur, 0);
> }
> 
> And the callers become:
> 
> 	if (sc->sa.ino_cur)
> 		xfs_scrub_xref_inode_check(sc, bno, len, &sc->sa.ino_cur);
> 
> I find that much easier to read...

<nod>

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 17/21] xfs: cross-reference reverse-mapping btree
  2018-01-15 23:04     ` Dave Chinner
@ 2018-01-16  6:38       ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:38 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 10:04:17AM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:24:49PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> 
> .....
> 
> > diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
> > index 2a58558..b4defa4 100644
> > --- a/fs/xfs/scrub/alloc.c
> > +++ b/fs/xfs/scrub/alloc.c
> > @@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
> >  
> >  	xfs_scrub_allocbt_xref_other(sc, agbno, len);
> >  	xfs_scrub_xref_not_inodes(sc, agbno, len);
> > +	xfs_scrub_xref_no_rmap(sc, agbno, len);
> 
> Hmmm - this is actually checking the rmap considers it free space,
> right? so rather than cross referencing is as "no rmap record"
> (which bleeds implementation into the API), wouldn't it be better
> to name this consistently with the other used/free space xref
> checks?  e.g. xfs_scrub_xref_rmap_is_free_space()?

Yes.

> > diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> > index d2b4747..ef7b461 100644
> > --- a/fs/xfs/scrub/bmap.c
> > +++ b/fs/xfs/scrub/bmap.c
> > @@ -99,6 +99,109 @@ 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_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 (!info->sc->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) {
> 
> What's this mean?

is_shared is true if the passed-in irec represents a data fork extent
of a reflink inode.  I'll add a comment to explain how we end up in this
block and why we have to use range lookup here.

> > +		error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, bno,
> > +				owner, offset, rflags, &rmap, &has_rmap);
> > +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> > +				&info->sc->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(info->sc->sa.rmap_cur, bno, 0, owner,
> > +				offset, rflags, &has_rmap);
> > +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> > +				&info->sc->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(info->sc->sa.rmap_cur, &rmap,
> > +				&has_rmap);
> > +		if (!xfs_scrub_should_check_xref(info->sc, &error,
> > +				&info->sc->sa.rmap_cur))
> > +			return;
> > +		if (!has_rmap) {
> > +			xfs_scrub_fblock_xref_set_corrupt(info->sc,
> > +					info->whichfork, irec->br_startoff);
> > +			return;
> > +		}
> > +	}
> 
> i.e. why is one branch doing a range lookup, and the other not?
> Perhaps this should also be put in a helper function....
> 
> > +
> > +	/* 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;
> > +	}
> 
> There are two unrelated things in the different branches. Shouldn't
> this irec mod be done right at the start where we are setting up for
> cow vs non-cow fork checks?

Ok.

> .....
> 
> > diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
> > index 68fea09..d3aaa6a 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(
> 
> xfs_scrub_count_rmap_ownedby_irec()

Ok.

> > +	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)))
> 
> Urk! that takes some parsing. Perhaps something like this?
> 
> 	bool irec_attr = (rec->rm_flags & XFS_RMAP_ATTR_FORK);
> 	bool oinfo_attr = (sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK);
> 
> 	if (rec->rm_owner != sroi->oinfo->oi_owner)
> 		return 0;
> 	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) ||
> 	    irec_attr == oinfo_attr)
> 		(*sroi->blocks) += rec->rm_blockcount;
> 	return 0;
> 
> ?

Yep.

> >  	}
> > +
> > +	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> > +	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
> >  }
> >  
> >  /* Is this chunk worth checking? */
> > @@ -228,6 +232,13 @@ xfs_scrub_iallocbt_check_freemask(
> >  			continue;
> >  		}
> >  
> > +		if (ir_holemask == 0)
> > +			xfs_scrub_xref_owned_by(bs->sc, agbno,
> > +					blks_per_cluster, &oinfo);
> > +		else
> > +			xfs_scrub_xref_not_owned_by(bs->sc, agbno,
> > +					blks_per_cluster, &oinfo);
> > +
> >  		/* If any part of this is a hole, skip it. */
> >  		if (ir_holemask)
> >  			continue;
> 
> I think these two conditions should be combined, along with an
> update to the comment about holes not containing inode chunks?

Ok.

> > @@ -266,6 +277,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 +314,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);
> 
> Line of whitespace to separate the corruption checks from the
> accounting logic?

Oops, sorry about that.

> >  
> >  	/* Handle non-sparse inodes */
> >  	if (!xfs_inobt_issparse(irec.ir_holemask)) {
> > @@ -347,6 +361,56 @@ 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;
> 
> Shouldn't a failure set some kind of corruption flag here rather
> than silently failing?

Yes.

> > +
> > +	if (xfs_sb_version_hasfinobt(&sc->mp->m_sb)) {
> > +		if (!sc->sa.fino_cur)
> > +			return;
> 
> Put this check at the start with the other cursor checks.

Ok.

> .....
> > @@ -355,10 +419,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);
> 
> Why do we only xref the inobt?

Hmm.  The goal of this function is to ensure that there are as many
OWN_INOBT blocks in the rmapbt as there are blocks in the inobt/finobt.

In theory we only need to check this once per scrub run (in which we're
guaranteed to scan both inode btrees) though from the perspective of
individual scrub ioctl calls, we probably ought to call this from either
scrubber for completeness.

> .....
> > diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
> > index 02704b0..8421c6e 100644
> > --- a/fs/xfs/scrub/rmap.c
> > +++ b/fs/xfs/scrub/rmap.c
> > @@ -157,3 +157,66 @@ 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,
> > +	xfs_agblock_t			bno,
> > +	xfs_extlen_t			len,
> > +	struct xfs_owner_info		*oinfo,
> > +	bool				fs_ok)
> 
> Not sure about this variable name. "record_should_exist"?

Yep.

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2018-01-15 23:49     ` Dave Chinner
@ 2018-01-16  6:49       ` Darrick J. Wong
  2018-01-16 19:47         ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:49 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 10:49:28AM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:25:17PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> >  fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 316 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> > index 700f8f1..df18e47 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.
> 
> This needs a comment somewhere in here describing the order of
> the records on the fragment list. AFAICT, it's ordered by start
> bno, but I'm not 100% sure and it seems the code is dependent on
> strict ordering of records the frag list....

Yes, the list must be ordered by agbno, which should be the case if we
iterated the rmap records in order.  We /could/ potentially list_sort
to ensure that this code doesn't blow up even if the rmapbt decides to
feed us out of order garbage.

Could?  Should.

> .....
> 
> > +	} 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);
> 
> I'm making the assumption here that we're seeing records in the
> order they are in the rmap tree and that it's in increase startblock
> order, hence the list is ordered that way....
> 
> > +	}
> > +
> > +	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;
> 
> .... and is where the code implies lowest to highest startblock
> ordering on the frag list.
> 
> > +		bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> > +		if (rbno > bno)
> > +			rbno = bno;
> 
> Can we put that check the other way around? we're looking for the
> shortest/smallest end block, so
> 
> 		if (bno < rbno)
> 			rbno = bno;
> 
> Makes a lot more sense to me.

Ok.

> 
> > +		list_del(&frag->list);
> > +		list_add_tail(&frag->list, &worklist);
> 
> list_move_tail()?
> 
> Ok, so we are moving fragments that start before the recount bno to
> the work list.

<nod>

> > +		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;
> 
> ok. so on error we clean up and free the frag list and work list....
> 
> > +
> > +	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) {
> 
> Ok, this needs to be clearer than it's walking the working set
> of fragments. I had to read this code several times before I worked
> out this is where the "working set" was being processed....

/* Walk the working set of rmap fragments... */

> 
> > +			bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> > +			if (bno != rbno) {
> > +				if (next_rbno > bno)
> > +					next_rbno = bno;
> 
> Same comment here about being a next_rbno "smallest bno" variable.

<nod>

> > +				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;
> 
> definitely assuming the frag list is ordered here :P
> 
> > +			list_del(&frag->list);
> > +			list_add_tail(&frag->list, &worklist);
> > +			if (next_rbno > bno)
> > +				next_rbno = bno;
> > +			nr--;
> > +			if (nr == 0)
> > +				break;
> > +		}
> 
> Ok, so if we get here with nr > 0, then we must have emptied the
> fragment list onto the work list, right? At this point, the outer
> loop will terminate. Don't we need to run the worklist processing
> loop one last time?

Nope.  Throughout xfs_scrub_refcountbt_process_rmap_fragments, we're
checking that the number of rmaps for a given refcount extent (i.e.
target_nr) remains the same.  nr is the number of rmaps we discarded
from the worklist at the top of the loop, so if we can't add the exact
same number of rmaps back to the worklist then we know that the refcount
is wrong.

So there /is/ a bug here, and the bug is that we need "if (nr) break;"
because we don't need to process more of the loop, we already know the
refcountbt is broken.

> > +		rbno = next_rbno;
> > +	}
> 
> .....
> > +/* 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;
> 
> This range query init feels like a familiar pattern now. Helper
> function (separate patch)?

Yeah, these can all get cleaned up at the end of the series.

> ....
> 
> > +/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> > +	    blocks != refcbt_blocks)
> > +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
> > +
> > +	if (!sc->sa.rmap_cur)
> > +		return;
> > +
> > +	/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> > +	    blocks != cow_blocks)
> > +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
> 
> Bit of a landmine that this code changes the owner info structure
> that was passed in....

Will fix.

> > +}
> > +
> >  /* 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);
> 
> .... because that's not obvious in this code here if we add anymore
> code after this call.

<nod>

> > +
> > +	return error;
> 
> error is zero here, so "return 0" instead?

Ok.

--D

> 
> Cheers,
> 
> Dave.
> 
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub
  2018-01-16  2:44     ` Dave Chinner
@ 2018-01-16  6:52       ` Darrick J. Wong
  2018-01-16 20:26         ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:52 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 01:44:47PM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:25:47PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> 
> ....
> 
> > +/* 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_bmbt_irec		*irec,
> > +	xfs_fsblock_t			bno)
> 
> What's in the irec if it's not the block number of the extent we
> are looking up?
> 
> Actually, I may have just answered that myself: bno is actually
> an agbno, not an fsbno. So that should be:
> 
> 	xfs_agblock_t			agbno)

Er... Yes.  Oops, will fix (all of the agbno).
> 
> > +{
> > +	xfs_agblock_t			fbno;
> > +	xfs_extlen_t			flen;
> > +	int				error;
> > +
> > +	if (!info->sc->sa.refc_cur)
> > +		return;
> > +
> > +	/* If this is shared, the inode flag must be set. */
> > +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> > +			irec->br_blockcount, &fbno, &flen, false);
> > +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> > +			&info->sc->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);
> 
> So is the block corrupt or is the inode flags corrupt? What
> determines the object we mark as corrupt/needing fixing here?

Hmm.  The reflink iflag is broken here, because the iflag is only supposed
to represent a shortcut for "might any of the data fork blocks shared?"
Will change to the ino_xref_set_corrupt helper.

--D

> > +/* 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_bmbt_irec		*irec,
> > +	xfs_fsblock_t			bno)
> 
> agbno
> 
> > +{
> > +	xfs_agblock_t			fbno;
> > +	xfs_extlen_t			flen;
> > +	int				error;
> > +
> > +	if (!info->sc->sa.refc_cur)
> > +		return;
> > +
> > +	/* No shared attr fork extents */
> > +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> > +			irec->br_blockcount, &fbno, &flen, false);
> > +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> > +			&info->sc->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_bmbt_irec		*irec,
> > +	xfs_fsblock_t			bno)
> 
> agbno
> 
> > +{
> > +	if (!info->sc->sa.refc_cur)
> > +		return;
> > +
> > +	xfs_scrub_xref_has_cow_staging(info->sc, bno, irec->br_blockcount);
> > +}
> > +
> >  /* Cross-reference a single rtdev extent record. */
> >  STATIC void
> >  xfs_scrub_bmap_rt_extent_xref(
> > @@ -243,6 +309,17 @@ xfs_scrub_bmap_extent_xref(
> >  	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
> >  	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
> >  	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
> > +	switch (info->whichfork) {
> > +	case XFS_DATA_FORK:
> > +		xfs_scrub_bmap_xref_refcount_data(info, irec, agbno);
> > +		break;
> > +	case XFS_ATTR_FORK:
> > +		xfs_scrub_bmap_xref_refcount_attr(info, irec, agbno);
> > +		break;
> > +	case XFS_COW_FORK:
> > +		xfs_scrub_bmap_xref_refcount_cow(info, irec, agbno);
> > +		break;
> 
> This is where the agbno comes from :P
> 
> > +	}
> >  
> >  	xfs_scrub_ag_free(info->sc, &info->sc->sa);
> >  }
> > diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> > index 30603b4..fb16314 100644
> > --- a/fs/xfs/scrub/ialloc.c
> > +++ b/fs/xfs/scrub/ialloc.c
> > @@ -97,6 +97,7 @@ xfs_scrub_iallocbt_chunk_xref(
> >  
> >  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> >  	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
> > +	xfs_scrub_xref_not_shared(sc, agbno, len);
> >  }
> >  
> >  /* Is this chunk worth checking? */
> > diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> > index fc546f2..f12586c 100644
> > --- a/fs/xfs/scrub/inode.c
> > +++ b/fs/xfs/scrub/inode.c
> > @@ -652,6 +652,7 @@ xfs_scrub_inode_xref(
> >  	xfs_scrub_inode_xref_finobt(sc, ino);
> >  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> >  	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
> > +	xfs_scrub_xref_not_shared(sc, agbno, 1);
> >  
> >  	xfs_scrub_ag_free(sc, &sc->sa);
> >  }
> > diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> > index df18e47..6e5b12a 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"
> > @@ -428,3 +429,67 @@ 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,
> > +	xfs_agblock_t			bno,
> 
> agbno
> 
> > +	xfs_extlen_t			len)
> > +{
> > +	struct xfs_refcount_irec	rc;
> > +	bool				has_cowflag;
> > +	int				has_refcount;
> > +	int				error;
> .....
> > +/*
> > + * 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,
> > +	xfs_agblock_t			bno,
> 
> agbno
> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 20/21] xfs: cross-reference the realtime bitmap
  2018-01-16  2:57     ` Dave Chinner
@ 2018-01-16  6:55       ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:55 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 01:57:46PM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:26:09PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> >  fs/xfs/libxfs/xfs_rtbitmap.c |   30 ++++++++++++++++++++++++++++++
> >  fs/xfs/scrub/bmap.c          |    3 +++
> >  fs/xfs/scrub/rtbitmap.c      |   20 ++++++++++++++++++++
> >  fs/xfs/scrub/scrub.h         |    6 ++++++
> >  fs/xfs/xfs_rtalloc.h         |    4 ++++
> >  5 files changed, 63 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,
> 
> shouldn't that be a xfs_extlen_t?
> 
> > +	bool				*is_free)
> > +{
> > +	xfs_rtblock_t			end;
> > +	xfs_extlen_t			clen;
> 
> clen? Not sure what that is shorthand for.

Clamped length, and it ought to be xfs_extlen_t as you point out.

> 
> > +	int				matches;
> > +	int				error;
> > +
> > +	*is_free = false;
> > +	while (len) {
> > +		clen = len > ~0U ? ~0U : len;
> 
> So we're checking for a len > UINT_MAX and clamping it here because
> the rt code uses 32 bit extent lengths internally? If we actually
> need 64 bit extent lengths, then isn't using UINT_MAX more obvious
> as to the purpose? i.e
> 
> 	if (len > UINT_MAX)
> 		clen = UINT_MAX;
> 	else
> 		clen = len;

Yes.
> 
> > +		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 5c34e025..2908a1b 100644
> > --- a/fs/xfs/scrub/bmap.c
> > +++ b/fs/xfs/scrub/bmap.c
> > @@ -278,6 +278,9 @@ xfs_scrub_bmap_rt_extent_xref(
> >  {
> >  	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> >  		return;
> > +
> > +	xfs_scrub_xref_not_rtfree(info->sc, irec->br_startblock,
> > +			irec->br_blockcount);
> 
> So, irec->br_blockcount is a xfs_extlen, which is 32 bits....
> 
> >  }
> >  
> >  /* Cross-reference a single datadev extent record. */
> > diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
> > index 6860d5d..1828b17 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)
> 
> So we're quietly converting it to a 64 bit length here. ANd not even
> using xfs_rtblock_t as you'd expect for a realtime block...
> 
> What caller needs/uses a 64 bit extent length? All the rt allocation
> code uses xfs_extlen_t for lengths, so I'm a bit confused as to why
> it's needed here.

I think I confused myself when wandering through the rt code... will fix.

--D

> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 21/21] xfs: cross-reference the block mappings when possible
  2018-01-16  2:58     ` Dave Chinner
@ 2018-01-16  6:55       ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16  6:55 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 01:58:55PM +1100, Dave Chinner wrote:
> On Tue, Jan 09, 2018 at 01:26:33PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> >  fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
> >  1 file changed, 34 insertions(+)
> 
> Looks fine.
> 
> Reviewed-by: Dave Chinner <dchinner@redhat.com>

Wooo, thanks for the review!

--D

> 
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2018-01-16  6:49       ` Darrick J. Wong
@ 2018-01-16 19:47         ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 19:47 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Mon, Jan 15, 2018 at 10:49:55PM -0800, Darrick J. Wong wrote:
> On Tue, Jan 16, 2018 at 10:49:28AM +1100, Dave Chinner wrote:
> > On Tue, Jan 09, 2018 at 01:25:17PM -0800, Darrick J. Wong wrote:
> > > 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>
> > > ---
> > > v2: streamline scrubber arguments, remove stack allocated objects
> > > ---
> > >  fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 316 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> > > index 700f8f1..df18e47 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.
> > 
> > This needs a comment somewhere in here describing the order of
> > the records on the fragment list. AFAICT, it's ordered by start
> > bno, but I'm not 100% sure and it seems the code is dependent on
> > strict ordering of records the frag list....
> 
> Yes, the list must be ordered by agbno, which should be the case if we
> iterated the rmap records in order.  We /could/ potentially list_sort
> to ensure that this code doesn't blow up even if the rmapbt decides to
> feed us out of order garbage.
> 
> Could?  Should.

Hm.  In theory the rmap should never feed us out of order rmaps, but if
it does that's an xref corruption, so we can employ a computationally
less expensive order check (over list_sort) and bail out if the list
is out of order.

> > .....
> > 
> > > +	} 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);
> > 
> > I'm making the assumption here that we're seeing records in the
> > order they are in the rmap tree and that it's in increase startblock
> > order, hence the list is ordered that way....
> > 
> > > +	}
> > > +
> > > +	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;
> > 
> > .... and is where the code implies lowest to highest startblock
> > ordering on the frag list.
> > 
> > > +		bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> > > +		if (rbno > bno)
> > > +			rbno = bno;
> > 
> > Can we put that check the other way around? we're looking for the
> > shortest/smallest end block, so
> > 
> > 		if (bno < rbno)
> > 			rbno = bno;
> > 
> > Makes a lot more sense to me.
> 
> Ok.
> 
> > 
> > > +		list_del(&frag->list);
> > > +		list_add_tail(&frag->list, &worklist);
> > 
> > list_move_tail()?
> > 
> > Ok, so we are moving fragments that start before the recount bno to
> > the work list.
> 
> <nod>
> 
> > > +		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;
> > 
> > ok. so on error we clean up and free the frag list and work list....
> > 
> > > +
> > > +	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) {
> > 
> > Ok, this needs to be clearer than it's walking the working set
> > of fragments. I had to read this code several times before I worked
> > out this is where the "working set" was being processed....
> 
> /* Walk the working set of rmap fragments... */

Correction: just update the comment to:
/* Discard any fragments ending at rbno from the worklist. */

--D

> 
> > 
> > > +			bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
> > > +			if (bno != rbno) {
> > > +				if (next_rbno > bno)
> > > +					next_rbno = bno;
> > 
> > Same comment here about being a next_rbno "smallest bno" variable.
> 
> <nod>
> 
> > > +				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;
> > 
> > definitely assuming the frag list is ordered here :P
> > 
> > > +			list_del(&frag->list);
> > > +			list_add_tail(&frag->list, &worklist);
> > > +			if (next_rbno > bno)
> > > +				next_rbno = bno;
> > > +			nr--;
> > > +			if (nr == 0)
> > > +				break;
> > > +		}
> > 
> > Ok, so if we get here with nr > 0, then we must have emptied the
> > fragment list onto the work list, right? At this point, the outer
> > loop will terminate. Don't we need to run the worklist processing
> > loop one last time?
> 
> Nope.  Throughout xfs_scrub_refcountbt_process_rmap_fragments, we're
> checking that the number of rmaps for a given refcount extent (i.e.
> target_nr) remains the same.  nr is the number of rmaps we discarded
> from the worklist at the top of the loop, so if we can't add the exact
> same number of rmaps back to the worklist then we know that the refcount
> is wrong.
> 
> So there /is/ a bug here, and the bug is that we need "if (nr) break;"
> because we don't need to process more of the loop, we already know the
> refcountbt is broken.
> 
> > > +		rbno = next_rbno;
> > > +	}
> > 
> > .....
> > > +/* 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;
> > 
> > This range query init feels like a familiar pattern now. Helper
> > function (separate patch)?
> 
> Yeah, these can all get cleaned up at the end of the series.
> 
> > ....
> > 
> > > +/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> > > +	    blocks != refcbt_blocks)
> > > +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
> > > +
> > > +	if (!sc->sa.rmap_cur)
> > > +		return;
> > > +
> > > +	/* 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_check_xref(sc, &error, &sc->sa.rmap_cur) &&
> > > +	    blocks != cow_blocks)
> > > +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
> > 
> > Bit of a landmine that this code changes the owner info structure
> > that was passed in....
> 
> Will fix.
> 
> > > +}
> > > +
> > >  /* 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);
> > 
> > .... because that's not obvious in this code here if we add anymore
> > code after this call.
> 
> <nod>
> 
> > > +
> > > +	return error;
> > 
> > error is zero here, so "return 0" instead?
> 
> Ok.
> 
> --D
> 
> > 
> > Cheers,
> > 
> > Dave.
> > 
> > -- 
> > Dave Chinner
> > david@fromorbit.com
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub
  2018-01-16  6:52       ` Darrick J. Wong
@ 2018-01-16 20:26         ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 20:26 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Mon, Jan 15, 2018 at 10:52:44PM -0800, Darrick J. Wong wrote:
> On Tue, Jan 16, 2018 at 01:44:47PM +1100, Dave Chinner wrote:
> > On Tue, Jan 09, 2018 at 01:25:47PM -0800, Darrick J. Wong wrote:
> > > 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>
> > > ---
> > > v2: streamline scrubber arguments, remove stack allocated objects
> > > ---
> > 
> > ....
> > 
> > > +/* 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_bmbt_irec		*irec,
> > > +	xfs_fsblock_t			bno)
> > 
> > What's in the irec if it's not the block number of the extent we
> > are looking up?
> > 
> > Actually, I may have just answered that myself: bno is actually
> > an agbno, not an fsbno. So that should be:
> > 
> > 	xfs_agblock_t			agbno)
> 
> Er... Yes.  Oops, will fix (all of the agbno).
> > 
> > > +{
> > > +	xfs_agblock_t			fbno;
> > > +	xfs_extlen_t			flen;
> > > +	int				error;
> > > +
> > > +	if (!info->sc->sa.refc_cur)
> > > +		return;
> > > +
> > > +	/* If this is shared, the inode flag must be set. */
> > > +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> > > +			irec->br_blockcount, &fbno, &flen, false);
> > > +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> > > +			&info->sc->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);
> > 
> > So is the block corrupt or is the inode flags corrupt? What
> > determines the object we mark as corrupt/needing fixing here?
> 
> Hmm.  The reflink iflag is broken here, because the iflag is only supposed
> to represent a shortcut for "might any of the data fork blocks shared?"
> Will change to the ino_xref_set_corrupt helper.

Thinking about these three functions a little more--

For the data fork, we only care that a non-reflink inode doesn't have shared
extents.

For the attr fork, we can call xfs_scrub_xref_is_not_shared directly.

For the cow fork, we can call xfs_scrub_xref_is_cow_staging directly.

The inode scrubber should be checking the reflink iflag, not the bmap
scrubber, so that should be refactored a bit too.

--D

> 
> --D
> 
> > > +/* 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_bmbt_irec		*irec,
> > > +	xfs_fsblock_t			bno)
> > 
> > agbno
> > 
> > > +{
> > > +	xfs_agblock_t			fbno;
> > > +	xfs_extlen_t			flen;
> > > +	int				error;
> > > +
> > > +	if (!info->sc->sa.refc_cur)
> > > +		return;
> > > +
> > > +	/* No shared attr fork extents */
> > > +	error = xfs_refcount_find_shared(info->sc->sa.refc_cur, bno,
> > > +			irec->br_blockcount, &fbno, &flen, false);
> > > +	if (!xfs_scrub_should_check_xref(info->sc, &error,
> > > +			&info->sc->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_bmbt_irec		*irec,
> > > +	xfs_fsblock_t			bno)
> > 
> > agbno
> > 
> > > +{
> > > +	if (!info->sc->sa.refc_cur)
> > > +		return;
> > > +
> > > +	xfs_scrub_xref_has_cow_staging(info->sc, bno, irec->br_blockcount);
> > > +}
> > > +
> > >  /* Cross-reference a single rtdev extent record. */
> > >  STATIC void
> > >  xfs_scrub_bmap_rt_extent_xref(
> > > @@ -243,6 +309,17 @@ xfs_scrub_bmap_extent_xref(
> > >  	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
> > >  	xfs_scrub_xref_not_inodes(info->sc, agbno, len);
> > >  	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
> > > +	switch (info->whichfork) {
> > > +	case XFS_DATA_FORK:
> > > +		xfs_scrub_bmap_xref_refcount_data(info, irec, agbno);
> > > +		break;
> > > +	case XFS_ATTR_FORK:
> > > +		xfs_scrub_bmap_xref_refcount_attr(info, irec, agbno);
> > > +		break;
> > > +	case XFS_COW_FORK:
> > > +		xfs_scrub_bmap_xref_refcount_cow(info, irec, agbno);
> > > +		break;
> > 
> > This is where the agbno comes from :P
> > 
> > > +	}
> > >  
> > >  	xfs_scrub_ag_free(info->sc, &info->sc->sa);
> > >  }
> > > diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> > > index 30603b4..fb16314 100644
> > > --- a/fs/xfs/scrub/ialloc.c
> > > +++ b/fs/xfs/scrub/ialloc.c
> > > @@ -97,6 +97,7 @@ xfs_scrub_iallocbt_chunk_xref(
> > >  
> > >  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> > >  	xfs_scrub_xref_owned_by(sc, agbno, len, &oinfo);
> > > +	xfs_scrub_xref_not_shared(sc, agbno, len);
> > >  }
> > >  
> > >  /* Is this chunk worth checking? */
> > > diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> > > index fc546f2..f12586c 100644
> > > --- a/fs/xfs/scrub/inode.c
> > > +++ b/fs/xfs/scrub/inode.c
> > > @@ -652,6 +652,7 @@ xfs_scrub_inode_xref(
> > >  	xfs_scrub_inode_xref_finobt(sc, ino);
> > >  	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
> > >  	xfs_scrub_xref_owned_by(sc, agbno, 1, &oinfo);
> > > +	xfs_scrub_xref_not_shared(sc, agbno, 1);
> > >  
> > >  	xfs_scrub_ag_free(sc, &sc->sa);
> > >  }
> > > diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
> > > index df18e47..6e5b12a 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"
> > > @@ -428,3 +429,67 @@ 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,
> > > +	xfs_agblock_t			bno,
> > 
> > agbno
> > 
> > > +	xfs_extlen_t			len)
> > > +{
> > > +	struct xfs_refcount_irec	rc;
> > > +	bool				has_cowflag;
> > > +	int				has_refcount;
> > > +	int				error;
> > .....
> > > +/*
> > > + * 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,
> > > +	xfs_agblock_t			bno,
> > 
> > agbno
> > 
> > Cheers,
> > 
> > Dave.
> > -- 
> > Dave Chinner
> > david@fromorbit.com
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2018-01-05 21:51   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:05     ` Darrick J. Wong
  2018-01-17  0:36       ` Dave Chinner
  0 siblings, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:05 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

On Fri, Jan 05, 2018 at 01:51:45PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: minor fixes suggested by dchinner

Ping?

--D

> ---
>  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 3b57ef0..decda7e 100644
> --- a/fs/xfs/libxfs/xfs_ialloc.c
> +++ b/fs/xfs/libxfs/xfs_ialloc.c
> @@ -2751,3 +2751,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_record;
> +	int			i;
> +	int			error;
> +
> +	*exists = false;
> +	error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has_record);
> +	while (error == 0 && has_record) {
> +		error = xfs_inobt_get_rec(cur, &irec, &has_record);
> +		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;
> +				return 0;
> +			}
> +		}
> +
> +		error = xfs_btree_increment(cur, 0, &has_record);
> +	}
> +	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)
> +		return error;
> +
> +	*count = ci.count;
> +	*freecount = ci.freecount;
> +	return 0;
> +}
> diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h
> index 66a8de0..c5402bb 100644
> --- a/fs/xfs/libxfs/xfs_ialloc.h
> +++ b/fs/xfs/libxfs/xfs_ialloc.h
> @@ -170,6 +170,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,
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 10/21] xfs: set up scrub cross-referencing helpers
  2018-01-05 21:54   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:06     ` Darrick J. Wong
  2018-01-17  0:41     ` Dave Chinner
  1 sibling, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:06 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

On Fri, Jan 05, 2018 at 01:54:51PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v2: pass corruption flags directly to helpers, reducing helper function count

Ping?

--D

> ---
>  fs/xfs/scrub/btree.c  |   71 +++++++++++++++++++++-----
>  fs/xfs/scrub/btree.h  |    9 +++
>  fs/xfs/scrub/common.c |  135 +++++++++++++++++++++++++++++++++++++++++++++----
>  fs/xfs/scrub/common.h |   16 ++++++
>  fs/xfs/scrub/scrub.c  |   10 ++++
>  fs/xfs/scrub/trace.h  |   22 ++++++++
>  6 files changed, 241 insertions(+), 22 deletions(-)
> 
> diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
> index df07661..e17d154 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,
> +	__u32				errflag,
> +	void				*ret_ip)
>  {
>  	if (*error == 0)
>  		return true;
> @@ -60,36 +62,80 @@ 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 |= errflag;
>  		*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,
> +			XFS_SCRUB_OFLAG_CORRUPT, __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,
> +			XFS_SCRUB_OFLAG_XFAIL, __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,
> +	__u32				errflag,
> +	void				*ret_ip)
>  {
> -	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
> +	sc->sm->sm_flags |= errflag;
>  
>  	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, XFS_SCRUB_OFLAG_CORRUPT,
> +			__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, XFS_SCRUB_OFLAG_XCORRUPT,
> +			__return_address);
>  }
>  
>  /*
> @@ -512,5 +558,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 d5c37d8..35c47cf 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,
> +	__u32				errflag,
> +	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 |= errflag;
>  		*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,
> +			XFS_SCRUB_OFLAG_CORRUPT, __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,
> +			XFS_SCRUB_OFLAG_XFAIL, __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,
> +	__u32				errflag,
> +	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 |= errflag;
>  		*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,
> +			XFS_SCRUB_OFLAG_CORRUPT, __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,
> +			XFS_SCRUB_OFLAG_XFAIL, __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.
> @@ -588,3 +668,38 @@ 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 (*error == 0)
> +		return true;
> +
> +	if (curpp) {
> +		/* If we've already given up on xref, just bail out. */
> +		if (!*curpp)
> +			return false;
> +
> +		/* xref error, delete cursor and bail out. */
> +		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
> +		*curpp = NULL;
> +	}
> +
> +	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
> +	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..11a5bd0 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,
> diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
> index cd46077..0ed2a12 100644
> --- a/fs/xfs/scrub/scrub.c
> +++ b/fs/xfs/scrub/scrub.c
> @@ -110,6 +110,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
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

* [PATCH v3 16/21] xfs: cross-reference inode btrees during scrub
  2017-12-23  0:44 ` [PATCH 16/21] xfs: cross-reference inode btrees during scrub Darrick J. Wong
  2018-01-09 21:22   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:23   ` Darrick J. Wong
  2018-01-17  0:44     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:23 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v3: fix some indenting craziness, clean up helpers per dave's suggestion
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   27 +++++++++++++++++
 fs/xfs/scrub/alloc.c    |    1 +
 fs/xfs/scrub/bmap.c     |    1 +
 fs/xfs/scrub/ialloc.c   |   73 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/inode.c    |   49 ++++++++++++++++++++++++++++++++
 fs/xfs/scrub/refcount.c |    1 +
 fs/xfs/scrub/rmap.c     |    4 +++
 fs/xfs/scrub/scrub.h    |    4 +++
 8 files changed, 160 insertions(+)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 1aba7c0..13ec76b 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -122,6 +122,7 @@ xfs_scrub_superblock_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -507,6 +508,7 @@ xfs_scrub_agf_xref(
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_agf_xref_freeblks(sc);
 	xfs_scrub_agf_xref_cntbt(sc);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -612,6 +614,7 @@ xfs_scrub_agfl_block_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 }
 
 /* Scrub an AGFL block. */
@@ -666,6 +669,7 @@ xfs_scrub_agfl_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -740,6 +744,27 @@ xfs_scrub_agfl(
 
 /* AGI */
 
+/* Check agi_count/agi_freecount */
+static inline void
+xfs_scrub_agi_xref_icounts(
+	struct xfs_scrub_context	*sc)
+{
+	struct xfs_agi			*agi = XFS_BUF_TO_AGI(sc->sa.agi_bp);
+	xfs_agino_t			icount;
+	xfs_agino_t			freecount;
+	int				error;
+
+	if (!sc->sa.ino_cur)
+		return;
+
+	error = xfs_ialloc_count_inodes(sc->sa.ino_cur, &icount, &freecount);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur))
+		return;
+	if (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);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_agi_xref(
@@ -759,6 +784,8 @@ xfs_scrub_agi_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
+	xfs_scrub_agi_xref_icounts(sc);
 
 	/* 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 836b3c3..0031014 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -104,6 +104,7 @@ xfs_scrub_allocbt_xref(
 		return;
 
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 7e8e239..6f1d145 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -138,6 +138,7 @@ xfs_scrub_bmap_extent_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
+	xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len);
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 4526894..bd7ba16 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -58,6 +58,35 @@ xfs_scrub_setup_ag_iallocbt(
 
 /* Inode btree scrubber. */
 
+/*
+ * 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.
+ */
+static inline void
+xfs_scrub_iallocbt_chunk_xref_other(
+	struct xfs_scrub_context	*sc,
+	struct xfs_inobt_rec_incore	*irec,
+	xfs_agino_t			agino)
+{
+	struct xfs_btree_cur		**pcur;
+	bool				has_irec;
+	int				error;
+
+	if (sc->sm->sm_type == XFS_SCRUB_TYPE_FINOBT)
+		pcur = &sc->sa.ino_cur;
+	else
+		pcur = &sc->sa.fino_cur;
+	if (!(*pcur))
+		return;
+	error = xfs_ialloc_has_inode_record(*pcur, agino, agino, &has_irec);
+	if (!xfs_scrub_should_check_xref(sc, &error, pcur))
+		return;
+	if (((irec->ir_freecount > 0 && !has_irec) ||
+	     (irec->ir_freecount == 0 && has_irec)))
+		xfs_scrub_btree_xref_set_corrupt(sc, *pcur, 0);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_iallocbt_chunk_xref(
@@ -71,6 +100,7 @@ xfs_scrub_iallocbt_chunk_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
+	xfs_scrub_iallocbt_chunk_xref_other(sc, irec, agino);
 }
 
 /* Is this chunk worth checking? */
@@ -352,3 +382,46 @@ xfs_scrub_finobt(
 {
 	return xfs_scrub_iallocbt(sc, XFS_BTNUM_FINO);
 }
+
+/* See if an inode btree has (or doesn't have) an inode chunk record. */
+static inline void
+xfs_scrub_xref_inode_check(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len,
+	struct xfs_btree_cur		**icur,
+	bool				should_have_inodes)
+{
+	bool				has_inodes;
+	int				error;
+
+	if (!(*icur))
+		return;
+
+	error = xfs_ialloc_has_inodes_at_extent(*icur, agbno, len, &has_inodes);
+	if (!xfs_scrub_should_check_xref(sc, &error, icur))
+		return;
+	if (has_inodes != should_have_inodes)
+		xfs_scrub_btree_xref_set_corrupt(sc, *icur, 0);
+}
+
+/* xref check that the extent is not covered by inodes */
+void
+xfs_scrub_xref_is_not_inode_chunk(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len)
+{
+	xfs_scrub_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, false);
+	xfs_scrub_xref_inode_check(sc, agbno, len, &sc->sa.fino_cur, false);
+}
+
+/* xref check that the extent is covered by inodes */
+void
+xfs_scrub_xref_is_inode_chunk(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len)
+{
+	xfs_scrub_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, true);
+}
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index be9cf19..b48d5fb 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -39,6 +39,7 @@
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
+#include "scrub/btree.h"
 #include "scrub/trace.h"
 
 /*
@@ -577,6 +578,53 @@ xfs_scrub_inode_map_raw(
 	return error;
 }
 
+/*
+ * Make sure the finobt doesn't think this inode is free.
+ * We don't have to check the inobt ourselves because we got the inode via
+ * IGET_UNTRUSTED, which checks the inobt for us.
+ */
+static void
+xfs_scrub_inode_xref_finobt(
+	struct xfs_scrub_context	*sc,
+	xfs_ino_t			ino)
+{
+	struct xfs_inobt_rec_incore	rec;
+	xfs_agino_t			agino;
+	int				has_record;
+	int				error;
+
+	if (!sc->sa.fino_cur)
+		return;
+
+	agino = XFS_INO_TO_AGINO(sc->mp, ino);
+
+	/*
+	 * Try to get the finobt record.  If we can't get it, then we're
+	 * in good shape.
+	 */
+	error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE,
+			&has_record);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
+	    !has_record)
+		return;
+
+	error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur) ||
+	    !has_record)
+		return;
+
+	/*
+	 * Otherwise, make sure this record either doesn't cover this inode,
+	 * or that it does but it's marked present.
+	 */
+	if (rec.ir_startino > agino ||
+	    rec.ir_startino + XFS_INODES_PER_CHUNK <= agino)
+		return;
+
+	if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_inode_xref(
@@ -599,6 +647,7 @@ xfs_scrub_inode_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
+	xfs_scrub_inode_xref_finobt(sc, ino);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 09a04ae..af54590 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -62,6 +62,7 @@ xfs_scrub_refcountbt_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
+	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
 }
 
 /* Scrub a refcountbt record. */
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 54b0eac..6e937ef 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -64,6 +64,10 @@ xfs_scrub_rmapbt_xref(
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
+	if (irec->rm_owner == XFS_RMAP_OWN_INODES)
+		xfs_scrub_xref_is_inode_chunk(sc, agbno, len);
+	else
+		xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
 }
 
 /* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index cbc6363..9b0033b 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -126,5 +126,9 @@ xfs_scrub_quota(struct xfs_scrub_context *sc)
 /* cross-referencing helpers */
 void xfs_scrub_xref_is_used_space(struct xfs_scrub_context *sc,
 		xfs_agblock_t agbno, xfs_extlen_t len);
+void xfs_scrub_xref_is_not_inode_chunk(struct xfs_scrub_context *sc,
+		xfs_agblock_t agbno, xfs_extlen_t len);
+void xfs_scrub_xref_is_inode_chunk(struct xfs_scrub_context *sc,
+		xfs_agblock_t agbno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v3 17/21] xfs: cross-reference reverse-mapping btree
  2017-12-23  0:44 ` [PATCH 17/21] xfs: cross-reference reverse-mapping btree Darrick J. Wong
  2018-01-09 21:24   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:25   ` Darrick J. Wong
  2018-01-17  0:52     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:25 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v3: fix indenting and standardize helper naming, refactor helpers per dave's suggestion
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   68 +++++++++++++++++++++++-
 fs/xfs/scrub/alloc.c    |    1 
 fs/xfs/scrub/bmap.c     |  134 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/btree.c    |    4 +
 fs/xfs/scrub/common.c   |   53 +++++++++++++++++++
 fs/xfs/scrub/common.h   |    4 +
 fs/xfs/scrub/ialloc.c   |  103 +++++++++++++++++++++++++++++++++++-
 fs/xfs/scrub/inode.c    |    4 +
 fs/xfs/scrub/rmap.c     |   65 +++++++++++++++++++++++
 fs/xfs/scrub/scrub.h    |    8 +++
 10 files changed, 440 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 13ec76b..1d109d5 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			agbno;
@@ -123,6 +125,8 @@ xfs_scrub_superblock_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -487,11 +491,58 @@ xfs_scrub_agf_xref_cntbt(
 		xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
 }
 
+/* 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_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 */
+	if (sc->sa.rmap_cur) {
+		error = xfs_btree_count_blocks(sc->sa.rmap_cur, &blocks);
+		if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+			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_check_xref(sc, &error, &sc->sa.bno_cur))
+		return;
+	btreeblks += blocks - 1;
+
+	error = xfs_btree_count_blocks(sc->sa.cnt_cur, &blocks);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.cnt_cur))
+		return;
+	btreeblks += blocks - 1;
+
+	if (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;
 	xfs_agblock_t			agbno;
 	int				error;
@@ -509,6 +560,9 @@ xfs_scrub_agf_xref(
 	xfs_scrub_agf_xref_freeblks(sc);
 	xfs_scrub_agf_xref_cntbt(sc);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_agf_xref_btreeblks(sc);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -599,6 +653,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;
@@ -608,13 +663,15 @@ struct xfs_scrub_agfl_info {
 STATIC void
 xfs_scrub_agfl_block_xref(
 	struct xfs_scrub_context	*sc,
-	xfs_agblock_t			agbno)
+	xfs_agblock_t			agbno,
+	struct xfs_owner_info		*oinfo)
 {
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 1, oinfo);
 }
 
 /* Scrub an AGFL block. */
@@ -634,7 +691,7 @@ xfs_scrub_agfl_block(
 	else
 		xfs_scrub_block_set_corrupt(sc, sc->sa.agfl_bp);
 
-	xfs_scrub_agfl_block_xref(sc, agbno);
+	xfs_scrub_agfl_block_xref(sc, agbno, priv);
 
 	return 0;
 }
@@ -655,6 +712,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			agbno;
 	int				error;
@@ -670,6 +728,8 @@ xfs_scrub_agfl_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -717,6 +777,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;
@@ -770,6 +831,7 @@ STATIC void
 xfs_scrub_agi_xref(
 	struct xfs_scrub_context	*sc)
 {
+	struct xfs_owner_info		oinfo;
 	struct xfs_mount		*mp = sc->mp;
 	xfs_agblock_t			agbno;
 	int				error;
@@ -786,6 +848,8 @@ xfs_scrub_agi_xref(
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 	xfs_scrub_agi_xref_icounts(sc);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 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 0031014..3faa437 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
 
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
+	xfs_scrub_xref_has_no_owner(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 6f1d145..933e0b8 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -99,6 +99,139 @@ struct xfs_scrub_bmap_info {
 	int				whichfork;
 };
 
+/* Look for a corresponding rmap for this irec. */
+static inline bool
+xfs_scrub_bmap_get_rmap(
+	struct xfs_scrub_bmap_info	*info,
+	struct xfs_bmbt_irec		*irec,
+	xfs_agblock_t			agbno,
+	uint64_t			owner,
+	struct xfs_rmap_irec		*rmap)
+{
+	xfs_fileoff_t			offset;
+	unsigned int			rflags = 0;
+	int				has_rmap;
+	int				error;
+
+	if (info->whichfork == XFS_ATTR_FORK)
+		rflags |= XFS_RMAP_ATTR_FORK;
+
+	/*
+	 * CoW staging extents are owned (on disk) by the refcountbt, so
+	 * their rmaps do not have offsets.
+	 */
+	if (info->whichfork == XFS_COW_FORK)
+		offset = 0;
+	else
+		offset = irec->br_startoff;
+
+	/*
+	 * If the caller thinks this could be a shared bmbt extent (IOWs,
+	 * any data fork extent of a reflink inode) then we have to use the
+	 * range rmap lookup to make sure we get the correct owner/offset.
+	 */
+	if (info->is_shared) {
+		error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
+				owner, offset, rflags, rmap, &has_rmap);
+		if (!xfs_scrub_should_check_xref(info->sc, &error,
+				&info->sc->sa.rmap_cur))
+			return false;
+		goto out;
+	}
+
+	/*
+	 * Otherwise, use the (faster) regular lookup.
+	 */
+	error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, 0, owner,
+			offset, rflags, &has_rmap);
+	if (!xfs_scrub_should_check_xref(info->sc, &error,
+			&info->sc->sa.rmap_cur))
+		return false;
+	if (!has_rmap)
+		goto out;
+
+	error = xfs_rmap_get_rec(info->sc->sa.rmap_cur, rmap, &has_rmap);
+	if (!xfs_scrub_should_check_xref(info->sc, &error,
+			&info->sc->sa.rmap_cur))
+		return false;
+
+out:
+	if (!has_rmap)
+		xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+			irec->br_startoff);
+	return has_rmap;
+}
+
+/* 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_bmbt_irec		*irec,
+	xfs_agblock_t			agbno)
+{
+	struct xfs_rmap_irec		rmap;
+	unsigned long long		rmap_end;
+	uint64_t			owner;
+
+	if (!info->sc->sa.rmap_cur)
+		return;
+
+	if (info->whichfork == XFS_COW_FORK)
+		owner = XFS_RMAP_OWN_COW;
+	else
+		owner = info->sc->ip->i_ino;
+
+	/* Find the rmap record for this irec. */
+	if (!xfs_scrub_bmap_get_rmap(info, irec, agbno, owner, &rmap))
+		return;
+
+	/* Check the rmap. */
+	rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
+	if (rmap.rm_startblock > agbno ||
+	    agbno + irec->br_blockcount > rmap_end)
+		xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+				irec->br_startoff);
+
+	/*
+	 * Check the logical offsets if applicable.  CoW staging extents
+	 * don't track logical offsets since the mappings only exist in
+	 * memory.
+	 */
+	if (info->whichfork != XFS_COW_FORK) {
+		rmap_end = (unsigned long long)rmap.rm_offset +
+				rmap.rm_blockcount;
+		if (rmap.rm_offset > irec->br_startoff ||
+		    irec->br_startoff + irec->br_blockcount > rmap_end)
+			xfs_scrub_fblock_xref_set_corrupt(info->sc,
+					info->whichfork, irec->br_startoff);
+	}
+
+	if (rmap.rm_owner != owner)
+		xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
+				irec->br_startoff);
+
+	/*
+	 * Check for discrepancies between the unwritten flag in the irec and
+	 * the rmap.  Note that the (in-memory) CoW fork distinguishes between
+	 * unwritten and written extents, but we don't track that in the rmap
+	 * records because the blocks are owned (on-disk) by the refcountbt,
+	 * which doesn't track unwritten state.
+	 */
+	if (owner != XFS_RMAP_OWN_COW &&
+	    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(
@@ -139,6 +272,7 @@ xfs_scrub_bmap_extent_xref(
 
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
 	xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len);
+	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
index 222e031..0589d4e 100644
--- a/fs/xfs/scrub/btree.c
+++ b/fs/xfs/scrub/btree.c
@@ -407,6 +407,10 @@ xfs_scrub_btree_check_block_owner(
 	if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
 		bs->cur = NULL;
 
+	xfs_scrub_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo);
+	if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
+		bs->cur = NULL;
+
 	if (init_sa)
 		xfs_scrub_ag_free(bs->sc, &bs->sc->sa);
 
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 68fea09..f5df8f2 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -325,6 +325,59 @@ 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_irec(
+	struct xfs_btree_cur			*cur,
+	struct xfs_rmap_irec			*rec,
+	void					*priv)
+{
+	struct xfs_scrub_rmap_ownedby_info	*sroi = priv;
+	bool					irec_attr;
+	bool					oinfo_attr;
+
+	irec_attr = rec->rm_flags & XFS_RMAP_ATTR_FORK;
+	oinfo_attr = sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK;
+
+	if (rec->rm_owner != sroi->oinfo->oi_owner)
+		return 0;
+
+	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) || irec_attr == oinfo_attr)
+		(*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_irec,
+			&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 84c302f..bf88a67 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 bd7ba16..1a16f78 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -96,11 +96,15 @@ xfs_scrub_iallocbt_chunk_xref(
 	xfs_agblock_t			agbno,
 	xfs_extlen_t			len)
 {
+	struct xfs_owner_info		oinfo;
+
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
 	xfs_scrub_iallocbt_chunk_xref_other(sc, irec, agino);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
+	xfs_scrub_xref_is_owned_by(sc, agbno, len, &oinfo);
 }
 
 /* Is this chunk worth checking? */
@@ -237,8 +241,14 @@ xfs_scrub_iallocbt_check_freemask(
 		}
 
 		/* If any part of this is a hole, skip it. */
-		if (ir_holemask)
+		if (ir_holemask) {
+			xfs_scrub_xref_is_not_owned_by(bs->sc, agbno,
+					blks_per_cluster, &oinfo);
 			continue;
+		}
+
+		xfs_scrub_xref_is_owned_by(bs->sc, agbno, blks_per_cluster,
+				&oinfo);
 
 		/* Grab the inode cluster buffer. */
 		imap.im_blkno = XFS_AGB_TO_DADDR(mp, bs->cur->bc_private.a.agno,
@@ -274,6 +284,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;
@@ -311,6 +322,9 @@ xfs_scrub_iallocbt_rec(
 	    (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)) {
 		len = XFS_B_TO_FSB(mp,
@@ -355,6 +369,72 @@ 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_btreeblks(
+	struct xfs_scrub_context	*sc,
+	int				which)
+{
+	struct xfs_owner_info		oinfo;
+	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 ||
+	    (xfs_sb_version_hasfinobt(&sc->mp->m_sb) && !sc->sa.fino_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 (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.ino_cur))
+		return;
+
+	if (sc->sa.fino_cur) {
+		error = xfs_btree_count_blocks(sc->sa.fino_cur, &finobt_blocks);
+		if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.fino_cur))
+			return;
+	}
+
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INOBT);
+	error = xfs_scrub_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, &oinfo,
+			&blocks);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (blocks != inobt_blocks + finobt_blocks)
+		xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
+}
+
+/*
+ * Make sure that the inobt records point to the same number of blocks as
+ * the rmap says are owned by inodes.
+ */
+STATIC void
+xfs_scrub_iallocbt_xref_rmap_inodes(
+	struct xfs_scrub_context	*sc,
+	int				which,
+	xfs_filblks_t			inode_blocks)
+{
+	struct xfs_owner_info		oinfo;
+	xfs_filblks_t			blocks;
+	int				error;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (blocks != inode_blocks)
+		xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
+}
+
 /* Scrub the inode btrees for some AG. */
 STATIC int
 xfs_scrub_iallocbt(
@@ -363,10 +443,29 @@ 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;
+
+	xfs_scrub_iallocbt_xref_rmap_btreeblks(sc, which);
+
+	/*
+	 * If we're scrubbing the inode btree, inode_blocks is the number of
+	 * blocks pointed to by all the inode chunk records.  Therefore, we
+	 * should compare to the number of inode chunk blocks that the rmap
+	 * knows about.  We can't do this for the finobt since it only points
+	 * to inode chunks with free inodes.
+	 */
+	if (which == XFS_BTNUM_INO)
+		xfs_scrub_iallocbt_xref_rmap_inodes(sc, which, inode_blocks);
+
+	return error;
 }
 
 int
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index b48d5fb..6e99577 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"
@@ -632,6 +633,7 @@ xfs_scrub_inode_xref(
 	xfs_ino_t			ino,
 	struct xfs_dinode		*dip)
 {
+	struct xfs_owner_info		oinfo;
 	xfs_agnumber_t			agno;
 	xfs_agblock_t			agbno;
 	int				error;
@@ -648,6 +650,8 @@ xfs_scrub_inode_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_inode_xref_finobt(sc, ino);
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
+	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 6e937ef..3ee5061 100644
--- a/fs/xfs/scrub/rmap.c
+++ b/fs/xfs/scrub/rmap.c
@@ -157,3 +157,68 @@ 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,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo,
+	bool				should_have_rmap)
+{
+	bool				has_rmap;
+	int				error;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo,
+			&has_rmap);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (has_rmap != should_have_rmap)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+}
+
+/* xref check that the extent is owned by a given owner */
+void
+xfs_scrub_xref_is_owned_by(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo)
+{
+	xfs_scrub_xref_check_owner(sc, bno, len, oinfo, true);
+}
+
+/* xref check that the extent is not owned by a given owner */
+void
+xfs_scrub_xref_is_not_owned_by(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len,
+	struct xfs_owner_info		*oinfo)
+{
+	xfs_scrub_xref_check_owner(sc, bno, len, oinfo, false);
+}
+
+/* xref check that the extent has no reverse mapping at all */
+void
+xfs_scrub_xref_has_no_owner(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			bno,
+	xfs_extlen_t			len)
+{
+	bool				has_rmap;
+	int				error;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (has_rmap)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+}
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 9b0033b..8fcf491 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -130,5 +130,13 @@ void xfs_scrub_xref_is_not_inode_chunk(struct xfs_scrub_context *sc,
 		xfs_agblock_t agbno, xfs_extlen_t len);
 void xfs_scrub_xref_is_inode_chunk(struct xfs_scrub_context *sc,
 		xfs_agblock_t agbno, xfs_extlen_t len);
+void xfs_scrub_xref_is_owned_by(struct xfs_scrub_context *sc,
+		xfs_agblock_t agbno, xfs_extlen_t len,
+		struct xfs_owner_info *oinfo);
+void xfs_scrub_xref_is_not_owned_by(struct xfs_scrub_context *sc,
+		xfs_agblock_t agbno, xfs_extlen_t len,
+		struct xfs_owner_info *oinfo);
+void xfs_scrub_xref_has_no_owner(struct xfs_scrub_context *sc,
+		xfs_agblock_t agbno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v3 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2017-12-23  0:44 ` [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:26   ` Darrick J. Wong
  2018-01-17  1:00     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:26 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v3: fix indenting and standardize helper naming, refactor helpers per dave'sr
    suggestion and add clarifying comments
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/refcount.c |  340 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 338 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index af54590..27e00fa 100644
--- a/fs/xfs/scrub/refcount.c
+++ b/fs/xfs/scrub/refcount.c
@@ -50,6 +50,295 @@ 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 (in agbno order) 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.  If the rmapbt
+		 * is healthy each rmap_irec we see will be in agbno order
+		 * so we don't need insertion sort here.
+		 */
+		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;
+
+	/* Make sure the fragments actually /are/ in agbno order. */
+	bno = 0;
+	list_for_each_entry(frag, &refchk->fragments, list) {
+		if (frag->rm.rm_startblock < bno)
+			goto done;
+		bno = frag->rm.rm_startblock;
+	}
+
+	/*
+	 * Find all the rmaps that start at or before the refc extent,
+	 * and put them on the worklist.
+	 */
+	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 (bno < rbno)
+			rbno = bno;
+		list_move_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 from the worklist. */
+		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 (bno < next_rbno)
+					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_move_tail(&frag->list, &worklist);
+			if (next_rbno > bno)
+				next_rbno = bno;
+			nr--;
+			if (nr == 0)
+				break;
+		}
+
+		/*
+		 * If we get here and nr > 0, this means that we added fewer
+		 * items to the worklist than we discarded because the fragment
+		 * list ran out of items.  Therefore, we cannot maintain the
+		 * required refcount.  Something is wrong, so we're done.
+		 */
+		if (nr)
+			goto done;
+
+		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_check_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(
@@ -63,6 +352,7 @@ xfs_scrub_refcountbt_xref(
 
 	xfs_scrub_xref_is_used_space(sc, agbno, len);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
+	xfs_scrub_refcountbt_xref_rmap(sc, agbno, len, refcount);
 }
 
 /* Scrub a refcountbt record. */
@@ -72,6 +362,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;
@@ -87,6 +378,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;
@@ -103,14 +396,57 @@ 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;
+
+	if (!sc->sa.rmap_cur)
+		return;
+
+	/* 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_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (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_check_xref(sc, &error, &sc->sa.rmap_cur))
+		return;
+	if (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;
+
+	xfs_scrub_refcount_xref_rmap(sc, &oinfo, cow_blocks);
+
+	return 0;
 }

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v3 19/21] xfs: cross-reference refcount btree during scrub
  2017-12-23  0:44 ` [PATCH 19/21] xfs: cross-reference refcount btree during scrub Darrick J. Wong
  2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:27   ` Darrick J. Wong
  2018-01-17  1:02     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v3: fix indenting and standardize helper naming, refactor helpers per dave's
    suggestion and add clarifying comments
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/scrub/agheader.c |   25 ++++++++++++++++++
 fs/xfs/scrub/alloc.c    |    1 +
 fs/xfs/scrub/bmap.c     |   15 +++++++++++
 fs/xfs/scrub/ialloc.c   |    1 +
 fs/xfs/scrub/inode.c    |   50 +++++++++++++++++++++++++----------
 fs/xfs/scrub/refcount.c |   67 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/rmap.c     |   37 ++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h    |    4 +++
 8 files changed, 186 insertions(+), 14 deletions(-)

diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 1d109d5..20a3beb 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -127,6 +127,7 @@ xfs_scrub_superblock_xref(
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 1);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -537,6 +538,25 @@ xfs_scrub_agf_xref_btreeblks(
 		xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
 }
 
+/* Check agf_refcount_blocks against tree size */
+static inline void
+xfs_scrub_agf_xref_refcblks(
+	struct xfs_scrub_context	*sc)
+{
+	struct xfs_agf			*agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
+	xfs_agblock_t			blocks;
+	int				error;
+
+	if (!sc->sa.refc_cur)
+		return;
+
+	error = xfs_btree_count_blocks(sc->sa.refc_cur, &blocks);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (blocks != be32_to_cpu(agf->agf_refcount_blocks))
+		xfs_scrub_block_xref_set_corrupt(sc, sc->sa.agf_bp);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_agf_xref(
@@ -563,6 +583,8 @@ xfs_scrub_agf_xref(
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
 	xfs_scrub_agf_xref_btreeblks(sc);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 1);
+	xfs_scrub_agf_xref_refcblks(sc);
 
 	/* scrub teardown will take care of sc->sa for us */
 }
@@ -672,6 +694,7 @@ xfs_scrub_agfl_block_xref(
 	xfs_scrub_xref_is_used_space(sc, agbno, 1);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 1);
 }
 
 /* Scrub an AGFL block. */
@@ -730,6 +753,7 @@ xfs_scrub_agfl_xref(
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 1);
 
 	/*
 	 * Scrub teardown will take care of sc->sa for us.  Leave sc->sa
@@ -850,6 +874,7 @@ xfs_scrub_agi_xref(
 	xfs_scrub_agi_xref_icounts(sc);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 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 3faa437..517c079 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -106,6 +106,7 @@ xfs_scrub_allocbt_xref(
 	xfs_scrub_allocbt_xref_other(sc, agbno, len);
 	xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
 	xfs_scrub_xref_has_no_owner(sc, agbno, len);
+	xfs_scrub_xref_is_not_shared(sc, agbno, len);
 }
 
 /* Scrub a bnobt/cntbt record. */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 933e0b8..7b2cf8f 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_rmap.h"
+#include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
@@ -273,6 +274,20 @@ xfs_scrub_bmap_extent_xref(
 	xfs_scrub_xref_is_used_space(info->sc, agbno, len);
 	xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len);
 	xfs_scrub_bmap_xref_rmap(info, irec, agbno);
+	switch (info->whichfork) {
+	case XFS_DATA_FORK:
+		if (xfs_is_reflink_inode(info->sc->ip))
+			break;
+		/* fall through */
+	case XFS_ATTR_FORK:
+		xfs_scrub_xref_is_not_shared(info->sc, agbno,
+				irec->br_blockcount);
+		break;
+	case XFS_COW_FORK:
+		xfs_scrub_xref_is_cow_staging(info->sc, agbno,
+				irec->br_blockcount);
+		break;
+	}
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 1a16f78..21c850a 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -105,6 +105,7 @@ xfs_scrub_iallocbt_chunk_xref(
 	xfs_scrub_iallocbt_chunk_xref_other(sc, irec, agino);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
 	xfs_scrub_xref_is_owned_by(sc, agbno, len, &oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, len);
 }
 
 /* Is this chunk worth checking? */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 6e99577..e92d633 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -652,22 +652,50 @@ xfs_scrub_inode_xref(
 	xfs_scrub_inode_xref_finobt(sc, ino);
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
 	xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
+	xfs_scrub_xref_is_not_shared(sc, agbno, 1);
 
 	xfs_scrub_ag_free(sc, &sc->sa);
 }
 
+/*
+ * If the reflink iflag disagrees with a scan for shared data fork extents,
+ * either flag an error (shared extents w/ no flag) or a preen (flag set w/o
+ * any shared extents).  We already checked for reflink iflag set on a non
+ * reflink filesystem.
+ */
+static void
+xfs_scrub_inode_check_reflink_iflag(
+	struct xfs_scrub_context	*sc,
+	xfs_ino_t			ino,
+	struct xfs_buf			*bp)
+{
+	struct xfs_mount		*mp = sc->mp;
+	bool				has_shared;
+	int				error;
+
+	if (!xfs_sb_version_hasreflink(&mp->m_sb))
+		return;
+
+	error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
+			&has_shared);
+	if (!xfs_scrub_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
+			XFS_INO_TO_AGBNO(mp, ino), &error))
+		return;
+	if (xfs_is_reflink_inode(sc->ip) && !has_shared)
+		xfs_scrub_ino_set_preen(sc, ino, bp);
+	else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
+		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+}
+
 /* Scrub an inode. */
 int
 xfs_scrub_inode(
 	struct xfs_scrub_context	*sc)
 {
 	struct xfs_dinode		di;
-	struct xfs_mount		*mp = sc->mp;
 	struct xfs_buf			*bp = NULL;
 	struct xfs_dinode		*dip;
 	xfs_ino_t			ino;
-
-	bool				has_shared;
 	int				error = 0;
 
 	/* Did we get the in-core inode, or are we doing this manually? */
@@ -692,18 +720,12 @@ xfs_scrub_inode(
 		goto out;
 
 	/*
-	 * Does this inode have the reflink flag set but no shared extents?
-	 * Set the preening flag if this is the case.
+	 * Look for discrepancies between file's data blocks and the reflink
+	 * iflag.  We already checked the iflag against the file mode when
+	 * we scrubbed the dinode.
 	 */
-	if (xfs_is_reflink_inode(sc->ip)) {
-		error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
-				&has_shared);
-		if (!xfs_scrub_xref_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
-				XFS_INO_TO_AGBNO(mp, ino), &error))
-			goto out;
-		if (!has_shared)
-			xfs_scrub_ino_set_preen(sc, ino, bp);
-	}
+	if (S_ISREG(VFS_I(sc->ip)->i_mode))
+		xfs_scrub_inode_check_reflink_iflag(sc, ino, bp);
 
 	xfs_scrub_inode_xref(sc, ino, dip);
 out:
diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c
index 27e00fa..31bc5ec 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"
@@ -450,3 +451,69 @@ xfs_scrub_refcountbt(
 
 	return 0;
 }
+
+/* xref check that a cow staging extent is marked in the refcountbt. */
+void
+xfs_scrub_xref_is_cow_staging(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len)
+{
+	struct xfs_refcount_irec	rc;
+	bool				has_cowflag;
+	int				has_refcount;
+	int				error;
+
+	if (!sc->sa.refc_cur)
+		return;
+
+	/* Find the CoW staging extent. */
+	error = xfs_refcount_lookup_le(sc->sa.refc_cur,
+			agbno + XFS_REFC_COW_START, &has_refcount);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (!has_refcount) {
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+		return;
+	}
+
+	error = xfs_refcount_get_rec(sc->sa.refc_cur, &rc, &has_refcount);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (!has_refcount) {
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 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, sc->sa.refc_cur, 0);
+
+	/* Must be at least as long as what was passed in */
+	if (rc.rc_blockcount < len)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+}
+
+/*
+ * xref check that the extent is not shared.  Only file data blocks
+ * can have multiple owners.
+ */
+void
+xfs_scrub_xref_is_not_shared(
+	struct xfs_scrub_context	*sc,
+	xfs_agblock_t			agbno,
+	xfs_extlen_t			len)
+{
+	bool				shared;
+	int				error;
+
+	if (!sc->sa.refc_cur)
+		return;
+
+	error = xfs_refcount_has_record(sc->sa.refc_cur, agbno, len, &shared);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (shared)
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+}
diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c
index 3ee5061..8f2a7c3 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,37 @@ 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_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 (!sc->sa.refc_cur)
+		return;
+
+	non_inode = XFS_RMAP_NON_INODE_OWNER(irec->rm_owner);
+	is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK;
+	is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK;
+	is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN;
+
+	/* If this is shared, must be a data fork extent. */
+	error = xfs_refcount_find_shared(sc->sa.refc_cur, irec->rm_startblock,
+			irec->rm_blockcount, &fbno, &flen, false);
+	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.refc_cur))
+		return;
+	if (flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten))
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+}
+
 /* Cross-reference with the other btrees. */
 STATIC void
 xfs_scrub_rmapbt_xref(
@@ -68,6 +100,11 @@ xfs_scrub_rmapbt_xref(
 		xfs_scrub_xref_is_inode_chunk(sc, agbno, len);
 	else
 		xfs_scrub_xref_is_not_inode_chunk(sc, agbno, len);
+	if (irec->rm_owner == XFS_RMAP_OWN_COW)
+		xfs_scrub_xref_is_cow_staging(sc, irec->rm_startblock,
+				irec->rm_blockcount);
+	else
+		xfs_scrub_rmapbt_xref_refc(sc, irec);
 }
 
 /* Scrub an rmapbt record. */
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index 8fcf491..c8f8b42 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -138,5 +138,9 @@ void xfs_scrub_xref_is_not_owned_by(struct xfs_scrub_context *sc,
 		struct xfs_owner_info *oinfo);
 void xfs_scrub_xref_has_no_owner(struct xfs_scrub_context *sc,
 		xfs_agblock_t agbno, xfs_extlen_t len);
+void xfs_scrub_xref_is_cow_staging(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
+void xfs_scrub_xref_is_not_shared(struct xfs_scrub_context *sc,
+		xfs_agblock_t bno, xfs_extlen_t len);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */

^ permalink raw reply related	[flat|nested] 94+ messages in thread

* [PATCH v3 20/21] xfs: cross-reference the realtime bitmap
  2017-12-23  0:44 ` [PATCH 20/21] xfs: cross-reference the realtime bitmap Darrick J. Wong
  2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
@ 2018-01-16 23:27   ` Darrick J. Wong
  2018-01-17  1:03     ` Dave Chinner
  1 sibling, 1 reply; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-16 23:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: Dave Chinner

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>
---
v3: fix indenting and standardize helper naming, refactor helpers per dave's
    suggestion and add clarifying comments
v2: streamline scrubber arguments, remove stack allocated objects
---
 fs/xfs/libxfs/xfs_rtbitmap.c |   21 +++++++++++++++++++++
 fs/xfs/scrub/bmap.c          |    3 +++
 fs/xfs/scrub/rtbitmap.c      |   23 +++++++++++++++++++++++
 fs/xfs/scrub/scrub.h         |    6 ++++++
 fs/xfs/xfs_rtalloc.h         |    4 ++++
 5 files changed, 57 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c
index 3fb29a5..106be2d 100644
--- a/fs/xfs/libxfs/xfs_rtbitmap.c
+++ b/fs/xfs/libxfs/xfs_rtbitmap.c
@@ -1097,3 +1097,24 @@ 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_extlen_t			len,
+	bool				*is_free)
+{
+	xfs_rtblock_t			end;
+	int				matches;
+	int				error;
+
+	error = xfs_rtcheck_range(mp, tp, start, len, 1, &end, &matches);
+	if (error)
+		return error;
+
+	*is_free = matches;
+	return 0;
+}
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 7b2cf8f..2bb3e0c 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -243,6 +243,9 @@ xfs_scrub_bmap_rt_extent_xref(
 {
 	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
+
+	xfs_scrub_xref_is_used_rt_space(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..2639099 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -98,3 +98,26 @@ 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_is_used_rt_space(
+	struct xfs_scrub_context	*sc,
+	xfs_rtblock_t			fsbno,
+	xfs_extlen_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_check_xref(sc, &error, NULL))
+		goto out_unlock;
+	if (is_free)
+		xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino,
+				NULL);
+out_unlock:
+	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 c8f8b42..0d92af8 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -142,5 +142,11 @@ void xfs_scrub_xref_is_cow_staging(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
 void xfs_scrub_xref_is_not_shared(struct xfs_scrub_context *sc,
 		xfs_agblock_t bno, xfs_extlen_t len);
+#ifdef CONFIG_XFS_RT
+void xfs_scrub_xref_is_used_rt_space(struct xfs_scrub_context *sc,
+		xfs_rtblock_t rtbno, xfs_extlen_t len);
+#else
+# define xfs_scrub_xref_is_used_rt_space(sc, rtbno, 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..dfee3c9 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_extlen_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] 94+ messages in thread

* Re: [PATCH v2 07/21] xfs: add scrub cross-referencing helpers for the inode btrees
  2018-01-16 23:05     ` Darrick J. Wong
@ 2018-01-17  0:36       ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  0:36 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:05:36PM -0800, Darrick J. Wong wrote:
> On Fri, Jan 05, 2018 at 01:51:45PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v2: minor fixes suggested by dchinner
> 
> Ping?

Oh, sorry, I missed this when going back through all the updates
that needed review...

.....
> > +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(

xfs_ialloc_count_inodes_rec(), like a recent comment I made
reviewing a later patch.

Otherwise looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>


-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v2 10/21] xfs: set up scrub cross-referencing helpers
  2018-01-05 21:54   ` [PATCH v2 " Darrick J. Wong
  2018-01-16 23:06     ` Darrick J. Wong
@ 2018-01-17  0:41     ` Dave Chinner
  1 sibling, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  0:41 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, Jan 05, 2018 at 01:54:51PM -0800, Darrick J. Wong wrote:
> 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>

.....

> @@ -512,5 +558,6 @@ xfs_scrub_btree(
>  	}
>  
>  out:
> +
>  	return error;
>  }

Stray whitespace.

Much nicer than the first version. :)

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 16/21] xfs: cross-reference inode btrees during scrub
  2018-01-16 23:23   ` [PATCH v3 " Darrick J. Wong
@ 2018-01-17  0:44     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  0:44 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:23:22PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v3: fix some indenting craziness, clean up helpers per dave's suggestion
> v2: streamline scrubber arguments, remove stack allocated objects
> ---

Looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 17/21] xfs: cross-reference reverse-mapping btree
  2018-01-16 23:25   ` [PATCH v3 " Darrick J. Wong
@ 2018-01-17  0:52     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  0:52 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:25:02PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v3: fix indenting and standardize helper naming, refactor helpers per dave's suggestion
> v2: streamline scrubber arguments, remove stack allocated objects
> ---

Looks ok.

Reviewed-by: Dave Chinner <dchinner@redhat.com>

-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2018-01-16 23:26   ` [PATCH v3 " Darrick J. Wong
@ 2018-01-17  1:00     ` Dave Chinner
  2018-01-17  1:11       ` Darrick J. Wong
  0 siblings, 1 reply; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  1:00 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:26:28PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v3: fix indenting and standardize helper naming, refactor helpers per dave'sr
>     suggestion and add clarifying comments
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/scrub/refcount.c |  340 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 338 insertions(+), 2 deletions(-)

One minor thing:

> +	while (!list_empty(&refchk->fragments)) {
> +		/* Discard any fragments ending at rbno from the worklist. */
> +		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 (bno < next_rbno)
> +					next_rbno = bno;
> +				continue;
> +			}
> +			list_del(&frag->list);
> +			kmem_free(frag);
> +			nr++;
> +		}
> +
> +		/* Empty list?  We're done. */
> +		if (list_empty(&refchk->fragments))
> +			break;

We haven't modified the &refchk->fragments list since the check at
the start of the loop, so this seems redundant and could be removed?

Other than that, I found this much easier to understand the second
time through. A bit of time for it to sink in and a few small
changes to logic and comments has done wonders :)

Reviewed-by: Dave Chinner <dchinner@redhat.com>

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 19/21] xfs: cross-reference refcount btree during scrub
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
@ 2018-01-17  1:02     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  1:02 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:27:15PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v3: fix indenting and standardize helper naming, refactor helpers per dave's
>     suggestion and add clarifying comments
> v2: streamline scrubber arguments, remove stack allocated objects

looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 20/21] xfs: cross-reference the realtime bitmap
  2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
@ 2018-01-17  1:03     ` Dave Chinner
  0 siblings, 0 replies; 94+ messages in thread
From: Dave Chinner @ 2018-01-17  1:03 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Jan 16, 2018 at 03:27:51PM -0800, Darrick J. Wong wrote:
> 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>
> ---
> v3: fix indenting and standardize helper naming, refactor helpers per dave's
>     suggestion and add clarifying comments
> v2: streamline scrubber arguments, remove stack allocated objects
> ---
>  fs/xfs/libxfs/xfs_rtbitmap.c |   21 +++++++++++++++++++++
>  fs/xfs/scrub/bmap.c          |    3 +++
>  fs/xfs/scrub/rtbitmap.c      |   23 +++++++++++++++++++++++
>  fs/xfs/scrub/scrub.h         |    6 ++++++
>  fs/xfs/xfs_rtalloc.h         |    4 ++++
>  5 files changed, 57 insertions(+)

Looks good.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 94+ messages in thread

* Re: [PATCH v3 18/21] xfs: cross-reference the rmapbt data with the refcountbt
  2018-01-17  1:00     ` Dave Chinner
@ 2018-01-17  1:11       ` Darrick J. Wong
  0 siblings, 0 replies; 94+ messages in thread
From: Darrick J. Wong @ 2018-01-17  1:11 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Wed, Jan 17, 2018 at 12:00:54PM +1100, Dave Chinner wrote:
> On Tue, Jan 16, 2018 at 03:26:28PM -0800, Darrick J. Wong wrote:
> > 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>
> > ---
> > v3: fix indenting and standardize helper naming, refactor helpers per dave'sr
> >     suggestion and add clarifying comments
> > v2: streamline scrubber arguments, remove stack allocated objects
> > ---
> >  fs/xfs/scrub/refcount.c |  340 +++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 338 insertions(+), 2 deletions(-)
> 
> One minor thing:
> 
> > +	while (!list_empty(&refchk->fragments)) {
> > +		/* Discard any fragments ending at rbno from the worklist. */
> > +		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 (bno < next_rbno)
> > +					next_rbno = bno;
> > +				continue;
> > +			}
> > +			list_del(&frag->list);
> > +			kmem_free(frag);
> > +			nr++;
> > +		}
> > +
> > +		/* Empty list?  We're done. */
> > +		if (list_empty(&refchk->fragments))
> > +			break;
> 
> We haven't modified the &refchk->fragments list since the check at
> the start of the loop, so this seems redundant and could be removed?

Yes, I think so.

> Other than that, I found this much easier to understand the second
> time through. A bit of time for it to sink in and a few small
> changes to logic and comments has done wonders :)
> 
> Reviewed-by: Dave Chinner <dchinner@redhat.com>

Hooray!  Thanks for the review!

--D

> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 94+ messages in thread

end of thread, other threads:[~2018-01-17  1:11 UTC | newest]

Thread overview: 94+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-23  0:42 [PATCH v11 00/21] xfs: online scrub xref support Darrick J. Wong
2017-12-23  0:42 ` [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl Darrick J. Wong
2018-01-05  1:12   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb Darrick J. Wong
2018-01-05  1:17   ` Dave Chinner
2018-01-05  1:24     ` Darrick J. Wong
2018-01-05  2:10       ` Dave Chinner
2017-12-23  0:43 ` [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext Darrick J. Wong
2018-01-05  1:17   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 04/21] xfs: always grab transaction when scrubbing inode Darrick J. Wong
2018-01-05  1:18   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode Darrick J. Wong
2018-01-05  1:23   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees Darrick J. Wong
2018-01-05  1:29   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees Darrick J. Wong
2018-01-05  1:36   ` Dave Chinner
2018-01-05  2:19     ` Darrick J. Wong
2018-01-05 21:51   ` [PATCH v2 " Darrick J. Wong
2018-01-16 23:05     ` Darrick J. Wong
2018-01-17  0:36       ` Dave Chinner
2017-12-23  0:43 ` [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees Darrick J. Wong
2018-01-05  1:40   ` Dave Chinner
2018-01-05  2:49     ` Darrick J. Wong
2018-01-05  3:38       ` Dave Chinner
2018-01-05 21:53   ` [PATCH v2 " Darrick J. Wong
2018-01-06 20:46     ` Dave Chinner
2017-12-23  0:43 ` [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees Darrick J. Wong
2018-01-05  1:41   ` Dave Chinner
2017-12-23  0:43 ` [PATCH 10/21] xfs: set up scrub cross-referencing helpers Darrick J. Wong
2018-01-05  2:08   ` Dave Chinner
2018-01-05  3:05     ` Darrick J. Wong
2018-01-05 21:54   ` [PATCH v2 " Darrick J. Wong
2018-01-16 23:06     ` Darrick J. Wong
2018-01-17  0:41     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers Darrick J. Wong
2018-01-05  2:11   ` Dave Chinner
2017-12-23  0:44 ` [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree Darrick J. Wong
2018-01-05  2:24   ` Dave Chinner
2018-01-05  2:53     ` Darrick J. Wong
2018-01-05  3:39       ` Dave Chinner
2017-12-23  0:44 ` [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs Darrick J. Wong
2018-01-08 23:36   ` Dave Chinner
2018-01-08 23:59     ` Darrick J. Wong
2018-01-09 21:00   ` [PATCH v2 " Darrick J. Wong
2018-01-10  0:12     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 14/21] xfs: cross-reference with the bnobt Darrick J. Wong
2018-01-08 23:51   ` Dave Chinner
2018-01-09  0:34     ` Darrick J. Wong
2018-01-09  0:57       ` Dave Chinner
2018-01-09 21:15   ` [PATCH v2 " Darrick J. Wong
2018-01-10  0:15     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 15/21] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
2018-01-08 23:55   ` Dave Chinner
2018-01-09  0:37     ` Darrick J. Wong
2018-01-09 21:20   ` [PATCH v2 " Darrick J. Wong
2018-01-10  0:19     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 16/21] xfs: cross-reference inode btrees during scrub Darrick J. Wong
2018-01-09 21:22   ` [PATCH v2 " Darrick J. Wong
2018-01-15 22:17     ` Dave Chinner
2018-01-16  6:30       ` Darrick J. Wong
2018-01-16 23:23   ` [PATCH v3 " Darrick J. Wong
2018-01-17  0:44     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 17/21] xfs: cross-reference reverse-mapping btree Darrick J. Wong
2018-01-09 21:24   ` [PATCH v2 " Darrick J. Wong
2018-01-15 23:04     ` Dave Chinner
2018-01-16  6:38       ` Darrick J. Wong
2018-01-16 23:25   ` [PATCH v3 " Darrick J. Wong
2018-01-17  0:52     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 18/21] xfs: cross-reference the rmapbt data with the refcountbt Darrick J. Wong
2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
2018-01-15 23:49     ` Dave Chinner
2018-01-16  6:49       ` Darrick J. Wong
2018-01-16 19:47         ` Darrick J. Wong
2018-01-16 23:26   ` [PATCH v3 " Darrick J. Wong
2018-01-17  1:00     ` Dave Chinner
2018-01-17  1:11       ` Darrick J. Wong
2017-12-23  0:44 ` [PATCH 19/21] xfs: cross-reference refcount btree during scrub Darrick J. Wong
2018-01-09 21:25   ` [PATCH v2 " Darrick J. Wong
2018-01-16  2:44     ` Dave Chinner
2018-01-16  6:52       ` Darrick J. Wong
2018-01-16 20:26         ` Darrick J. Wong
2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
2018-01-17  1:02     ` Dave Chinner
2017-12-23  0:44 ` [PATCH 20/21] xfs: cross-reference the realtime bitmap Darrick J. Wong
2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
2018-01-16  2:57     ` Dave Chinner
2018-01-16  6:55       ` Darrick J. Wong
2018-01-16 23:27   ` [PATCH v3 " Darrick J. Wong
2018-01-17  1:03     ` Dave Chinner
2017-12-23  0:45 ` [PATCH 21/21] xfs: cross-reference the block mappings when possible Darrick J. Wong
2018-01-09 21:26   ` [PATCH v2 " Darrick J. Wong
2018-01-16  2:58     ` Dave Chinner
2018-01-16  6:55       ` Darrick J. Wong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.