Skip to contentSkip to conversation

xfs: online scrub xref support

PATCH Superseded Darrick J. Wong 2 participants 94 messages Dec 23, 2017 – Jan 17, 2018 XFS filesystem

v11 of 22 Superseded Latest is v22 →

lore
Share & tools
lore
#0 lore raw

v11 cover xfs: online scrub xref support

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

#1 lore raw

PATCH v11 1/21 xfs: ignore agfl read errors when not scrubbing agfl

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/common.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/common.c +1 -1
index 98452ad..6ec4e10 100644
@@ -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;
 }
Reviewed-by on v11 1/21
#2 lore raw

Re: [PATCH 01/21] xfs: ignore agfl read errors when not scrubbing agfl

Quoted 7 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

Looks fine.

Signature · 2 lines
Dave Chinner
[email protected]
#3 lore raw

PATCH v11 2/21 xfs: catch a few more error codes when scrubbing secondary sb

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
fs/xfs/scrub/agheader.c +16 -0
index b599358..97beb47 100644
@@ -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;
 
Reviewed-by on v11 2/21
#4 lore raw

Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb

Quoted 29 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
fs/xfs/scrub/agheader.c +8 -0
index b599358..97beb47 100644
@@ -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.
+	 */

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 146

Quoted 10 lines
fs/xfs/scrub/agheader.c +8 -0
+	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

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.

Signature · 2 lines
Dave Chinner
[email protected]
#5 lore raw

Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb

Quoted 43 lines · Dave Chinner
Quoted 29 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
fs/xfs/scrub/agheader.c +8 -0
index b599358..97beb47 100644
@@ -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.
+	 */

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 146

Quoted 10 lines
fs/xfs/scrub/agheader.c +8 -0
+	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

Quoted 8 lines

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

Quoted 11 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#6 lore raw

Re: [PATCH 02/21] xfs: catch a few more error codes when scrubbing secondary sb

Quoted 51 lines · Darrick J. Wong
Quoted 50 lines · Dave Chinner
Quoted 29 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/agheader.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
fs/xfs/scrub/agheader.c +8 -0
index b599358..97beb47 100644
@@ -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.
+	 */

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 146

Quoted 10 lines
fs/xfs/scrub/agheader.c +8 -0
+	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

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

Quoted 5 lines
Quoted 1 line

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.

Signature · 2 lines
Dave Chinner
[email protected]
#7 lore raw

PATCH v11 3/21 xfs: xfs_scrub_bmap should use for_each_xfs_iext

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Refactor xfs_scrub_bmap to use for_each_xfs_iext now that it exists.

---
 fs/xfs/scrub/bmap.c |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
fs/xfs/scrub/bmap.c +1 -4
index 42fec0b..0261e11 100644
@@ -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))
Reviewed-by on v11 3/21
#8 lore raw

Re: [PATCH 03/21] xfs: xfs_scrub_bmap should use for_each_xfs_iext

Quoted 23 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Refactor xfs_scrub_bmap to use for_each_xfs_iext now that it exists.

---
 fs/xfs/scrub/bmap.c |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
fs/xfs/scrub/bmap.c +0 -1
index 42fec0b..0261e11 100644
@@ -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);

fs/xfs/scrub/bmap.c xfs_scrub_bmap( · line 319

Quoted 10 lines
fs/xfs/scrub/bmap.c +1 -3
@@ -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.

Signature · 2 lines
Dave Chinner
[email protected]
#9 lore raw

PATCH v11 4/21 xfs: always grab transaction when scrubbing inode

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/inode.c +1 -1
index 2be4b25..61cd112 100644
@@ -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 on v11 4/21
#10 lore raw

Re: [PATCH 04/21] xfs: always grab transaction when scrubbing inode

Quoted 19 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/inode.c +0 -0
index 2be4b25..61cd112 100644

fs/xfs/scrub/inode.c xfs_scrub_setup_inode( · line 70

Quoted 8 lines
fs/xfs/scrub/inode.c +1 -1
@@ -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;
 	}
Signature · 2 lines
Dave Chinner
[email protected]
#11 lore raw

PATCH v11 5/21 xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/common.c |   32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)
fs/xfs/scrub/common.c +28 -4
index 6ec4e10..d5c37d8 100644
@@ -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),
Reviewed-by on v11 5/21
#12 lore raw

Re: [PATCH 05/21] xfs: distinguish between corrupt inode and invalid inum in xfs_scrub_get_inode

Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

Quoted 5 lines

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.

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

Signature · 2 lines
Dave Chinner
[email protected]
#13 lore raw

PATCH v11 6/21 xfs: add scrub cross-referencing helpers for the free space btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
fs/xfs/libxfs/xfs_alloc.c +20 -1
index 83ed771..82333fc 100644
@@ -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);
+}
fs/xfs/libxfs/xfs_alloc.h +10 -0
index 7ba2d12..65a0caf 100644
@@ -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__ */
fs/xfs/libxfs/xfs_btree.c +29 -0
index 5f33adf..e0bdff3 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_btree.h +2 -0
index b57501c..551a2a0 100644
@@ -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__ */
Reviewed-by on v11 6/21
#14 lore raw

Re: [PATCH 06/21] xfs: add scrub cross-referencing helpers for the free space btrees

Quoted 7 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

minor nit:

Quoted 7 lines
fs/xfs/libxfs/xfs_btree.c +7 -0
+/* 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)

fs/xfs/libxfs/xfs_btree.c xfs_btree_diff_two_ptrs( · line 4983

Quoted 10 lines
fs/xfs/libxfs/xfs_btree.c +10 -0
+{
+	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.

Signature · 2 lines
Dave Chinner
[email protected]
#15 lore raw

PATCH v11 7/21 xfs: add scrub cross-referencing helpers for the inode btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
 fs/xfs/libxfs/xfs_ialloc.c |   99 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ialloc.h |    6 +++
 2 files changed, 105 insertions(+)
fs/xfs/libxfs/xfs_ialloc.c +99 -0
index 3b57ef0..5de1ef3 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_ialloc.h +6 -0
index 66a8de0..c5402bb 100644
@@ -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,
#16 lore raw

Re: [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
 fs/xfs/libxfs/xfs_ialloc.c |   99 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ialloc.h |    6 +++
 2 files changed, 105 insertions(+)

Some minor things.

Quoted 11 lines
fs/xfs/libxfs/xfs_ialloc.c +3 -0
index 3b57ef0..5de1ef3 100644
@@ -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

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2766

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+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

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+	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;
+

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2786

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+		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.

Quoted 55 lines
fs/xfs/libxfs/xfs_ialloc.c +55 -0
+			}
+		}
+
+		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)
+{

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2851

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+	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.

Signature · 2 lines
Dave Chinner
[email protected]
#17 lore raw

Re: [PATCH 07/21] xfs: add scrub cross-referencing helpers for the inode btrees

Quoted 29 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
 fs/xfs/libxfs/xfs_ialloc.c |   99 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ialloc.h |    6 +++
 2 files changed, 105 insertions(+)

Some minor things.

Quoted 15 lines
fs/xfs/libxfs/xfs_ialloc.c +7 -0
index 3b57ef0..5de1ef3 100644
@@ -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,
Quoted 8 lines

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2766

Quoted 6 lines
fs/xfs/libxfs/xfs_ialloc.c +6 -0
+	bool			*exists)
+{
+	struct xfs_inobt_rec_incore	irec;
+	xfs_agino_t		agino;
+	uint16_t		holemask;
+	int			has;

has_record

Fixed.

Quoted 23 lines
Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+	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;
+

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2786

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+		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;

Quoted 1 line

Then the out label can go away.

Fixed.

Quoted 69 lines
Quoted 55 lines
fs/xfs/libxfs/xfs_ialloc.c +55 -0
+			}
+		}
+
+		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)
+{

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2851

Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10 -0
+	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:

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

Fixed.

--D

Quoted 11 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#18 lore raw

PATCH v2 7/21 xfs: add scrub cross-referencing helpers for the inode btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
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(+)
fs/xfs/libxfs/xfs_ialloc.c +99 -0
index 3b57ef0..decda7e 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_ialloc.h +6 -0
index 66a8de0..c5402bb 100644
@@ -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,
#19 lore raw

Re: [PATCH v2 07/21] xfs: add scrub cross-referencing helpers for the inode btrees

Quoted 8 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
v2: minor fixes suggested by dchinner

Ping?

--D

Quoted 133 lines
---
 fs/xfs/libxfs/xfs_ialloc.c |   99 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ialloc.h |    6 +++
 2 files changed, 105 insertions(+)
fs/xfs/libxfs/xfs_ialloc.c +99 -0
index 3b57ef0..decda7e 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_ialloc.h +6 -1
index 66a8de0..c5402bb 100644
@@ -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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Reviewed-by
#20 lore raw

Re: [PATCH v2 07/21] xfs: add scrub cross-referencing helpers for the inode btrees

Quoted 11 lines · Darrick J. Wong
Quoted 8 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the inode btrees that will be used to cross-reference metadata against the inobt.

---
v2: minor fixes suggested by dchinner

Ping?

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

.....

Quoted 8 lines

fs/xfs/libxfs/xfs_ialloc.c xfs_verify_dir_ino( · line 2819

Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +8 -0
+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.

Signature · 2 lines
Dave Chinner
[email protected]
#21 lore raw

PATCH v11 8/21 xfs: add scrub cross-referencing helpers for the rmap btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
 fs/xfs/libxfs/xfs_rmap.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_rmap.h |    5 ++++
 2 files changed, 63 insertions(+)
fs/xfs/libxfs/xfs_rmap.c +58 -0
index 50db920..ea78ec3 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_rmap.h +5 -0
index 0fcd5b1..380e53b 100644
@@ -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__ */
#22 lore raw

Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees

Quoted 43 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
 fs/xfs/libxfs/xfs_rmap.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_rmap.h |    5 ++++
 2 files changed, 63 insertions(+)
fs/xfs/libxfs/xfs_rmap.c +23 -0
index 50db920..ea78ec3 100644
@@ -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(

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2422

Quoted 10 lines
fs/xfs/libxfs/xfs_rmap.c +10 -0
+	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?

Quoted 13 lines
fs/xfs/libxfs/xfs_rmap.c +13 -0
+	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;
+	}
+

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2445

Quoted 10 lines
fs/xfs/libxfs/xfs_rmap.c +10 -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.

Signature · 2 lines
Dave Chinner
[email protected]
#23 lore raw

Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees

Quoted 48 lines · Dave Chinner
Quoted 47 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
 fs/xfs/libxfs/xfs_rmap.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_rmap.h |    5 ++++
 2 files changed, 63 insertions(+)
fs/xfs/libxfs/xfs_rmap.c +27 -0
index 50db920..ea78ec3 100644
@@ -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,
Quoted 8 lines

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2422

Quoted 6 lines
fs/xfs/libxfs/xfs_rmap.c +6 -0
+	bool			*has_rmap)
+{
+	uint64_t		owner;
+	uint64_t		offset;
+	unsigned int		flags;
+	int			stat;

has_record to match the other code?

Fixed.

Quoted 19 lines
Quoted 19 lines
fs/xfs/libxfs/xfs_rmap.c +19 -0
+	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;
Quoted 8 lines

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2445

Quoted 4 lines
fs/xfs/libxfs/xfs_rmap.c +4 -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

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#24 lore raw

Re: [PATCH 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees

Quoted 101 lines · Darrick J. Wong
Quoted 48 lines · Dave Chinner
Quoted 47 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
 fs/xfs/libxfs/xfs_rmap.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_rmap.h |    5 ++++
 2 files changed, 63 insertions(+)
fs/xfs/libxfs/xfs_rmap.c +27 -0
index 50db920..ea78ec3 100644
@@ -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,
Quoted 8 lines

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2422

Quoted 6 lines
fs/xfs/libxfs/xfs_rmap.c +6 -0
+	bool			*has_rmap)
+{
+	uint64_t		owner;
+	uint64_t		offset;
+	unsigned int		flags;
+	int			stat;

has_record to match the other code?

Fixed.

Quoted 19 lines
Quoted 19 lines
fs/xfs/libxfs/xfs_rmap.c +19 -0
+	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;
Quoted 8 lines

fs/xfs/libxfs/xfs_rmap.c xfs_rmap_compare( · line 2445

Quoted 4 lines
fs/xfs/libxfs/xfs_rmap.c +4 -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."

Quoted 4 lines

& 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.

Signature · 2 lines
Dave Chinner
[email protected]
#25 lore raw

PATCH v2 8/21 xfs: add scrub cross-referencing helpers for the rmap btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
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(+)
fs/xfs/libxfs/xfs_rmap.c +67 -0
index 50db920..79822cf 100644
@@ -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;
+}
fs/xfs/libxfs/xfs_rmap.h +5 -0
index 0fcd5b1..380e53b 100644
@@ -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__ */
Reviewed-by on v2 8/21
#26 lore raw

Re: [PATCH v2 08/21] xfs: add scrub cross-referencing helpers for the rmap btrees

Quoted 8 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the rmap btrees that will be used to cross-reference metadata against the rmapbt.

---
v2: expand documentation to clarify the behavior of _record_exists

Looks good now.

Signature · 2 lines
Dave Chinner
[email protected]
#27 lore raw

PATCH v11 9/21 xfs: add scrub cross-referencing helpers for the refcount btrees

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the refcount btrees that will be used to cross-reference metadata against the refcountbt.

---
 fs/xfs/libxfs/xfs_refcount.c |   19 +++++++++++++++++++
 fs/xfs/libxfs/xfs_refcount.h |    3 +++
 2 files changed, 22 insertions(+)
fs/xfs/libxfs/xfs_refcount.c +19 -0
index c40d267..bee68c2 100644
@@ -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);
+}
fs/xfs/libxfs/xfs_refcount.h +3 -0
index eafb9d1..2a731ac 100644
@@ -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__ */
Reviewed-by on v11 9/21
#28 lore raw

Re: [PATCH 09/21] xfs: add scrub cross-referencing helpers for the refcount btrees

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Add a couple of functions to the refcount btrees that will be used to cross-reference metadata against the refcountbt.

---
 fs/xfs/libxfs/xfs_refcount.c |   19 +++++++++++++++++++
 fs/xfs/libxfs/xfs_refcount.h |    3 +++
 2 files changed, 22 insertions(+)

looks fine.

Signature · 2 lines
Dave Chinner
[email protected]
#29 lore raw

PATCH v11 10/21 xfs: set up scrub cross-referencing helpers

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

---
 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(-)
fs/xfs/scrub/btree.c +60 -12
index df07661..36cff8f 100644
@@ -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;
 }
fs/xfs/scrub/btree.h +9 -0
index 4de825a6..e2b868e 100644
@@ -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,
fs/xfs/scrub/common.c +128 -10

202 lines not on this page · raw message

fs/xfs/scrub/common.h +28 -0
index fe12053..d7ee72a 100644
@@ -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__ */
fs/xfs/scrub/scrub.c +10 -0
index cd46077..0ed2a12 100644
@@ -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.
  */
 
 /*
fs/xfs/scrub/trace.h +22 -0
index c4ebfb5..81becf6 100644
@@ -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
#30 lore raw

Re: [PATCH 10/21] xfs: set up scrub cross-referencing helpers

Quoted 14 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

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

....

Quoted 15 lines
fs/xfs/scrub/btree.c +5 -3
-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;

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 65

Quoted 5 lines
fs/xfs/scrub/btree.c +1 -1
@@ -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?

Quoted 15 lines
fs/xfs/scrub/btree.c +2 -2
 		*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;
 }

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 88

Quoted 10 lines
fs/xfs/scrub/btree.c +9 -0
 
+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);

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 99

Quoted 11 lines
fs/xfs/scrub/btree.c +11 -0
+}
+
+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);
Quoted 37 lines
fs/xfs/scrub/btree.c +22 -6
+}
+
 /* 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);

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 140

Quoted 10 lines
fs/xfs/scrub/btree.c +9 -0
+}
+
+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).

Quoted 13 lines
fs/xfs/scrub/common.c +13 -0
+
+/*
+ * 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. */

fs/xfs/scrub/common.c xfs_scrub_setup_inode_contents( · line 693

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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?

fs/xfs/scrub/common.c xfs_scrub_setup_inode_contents( · line 698

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	/* 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;
	}
Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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;
+}

.....

Quoted 5 lines
fs/xfs/scrub/common.h +1 -0
@@ -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);
 
+/*

fs/xfs/scrub/common.h int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in); · line 168

Quoted 10 lines
fs/xfs/scrub/common.h +10 -0
+ * 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.

Signature · 2 lines
Dave Chinner
[email protected]
#31 lore raw

Re: [PATCH 10/21] xfs: set up scrub cross-referencing helpers

Quoted 41 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

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

....

Quoted 15 lines
fs/xfs/scrub/btree.c +5 -3
-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;

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 65

Quoted 5 lines
fs/xfs/scrub/btree.c +1 -1
@@ -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.

Quoted 2 lines

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

Good idea!

Quoted 89 lines
Quoted 15 lines
fs/xfs/scrub/btree.c +2 -2
 		*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;
 }

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 88

Quoted 10 lines
fs/xfs/scrub/btree.c +9 -0
 
+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);

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 99

Quoted 11 lines
fs/xfs/scrub/btree.c +11 -0
+}
+
+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);
Quoted 41 lines
fs/xfs/scrub/btree.c +26 -6
+}
+
 /* 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(
Quoted 8 lines

fs/xfs/scrub/btree.c xfs_scrub_btree_process_error( · line 140

Quoted 6 lines
fs/xfs/scrub/btree.c +5 -0
+	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.

Quoted 34 lines
Quoted 13 lines
fs/xfs/scrub/common.c +13 -0
+
+/*
+ * 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. */

fs/xfs/scrub/common.c xfs_scrub_setup_inode_contents( · line 693

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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?

fs/xfs/scrub/common.c xfs_scrub_setup_inode_contents( · line 698

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	/* 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:

Quoted 7 lines
	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...)

Quoted 24 lines
		/* xref error, delete cursor and bail out. */
		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
		*curpp = NULL;
	}
Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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;
+}

.....

Quoted 4 lines
fs/xfs/scrub/common.h +0 -0
@@ -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);
 
Quoted 14 lines

fs/xfs/scrub/common.h int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in); · line 168

Quoted 11 lines
fs/xfs/scrub/common.h +11 -0
+/*
+ * 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

Quoted 11 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#32 lore raw

PATCH v2 10/21 xfs: set up scrub cross-referencing helpers

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

---
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(-)
fs/xfs/scrub/btree.c +59 -12
index df07661..e17d154 100644
@@ -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;
 }
fs/xfs/scrub/btree.h +9 -0
index 4de825a6..e2b868e 100644
@@ -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,
fs/xfs/scrub/common.c +125 -10

199 lines not on this page · raw message

fs/xfs/scrub/common.h +16 -0
index fe12053..11a5bd0 100644
@@ -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,
fs/xfs/scrub/scrub.c +10 -0
index cd46077..0ed2a12 100644
@@ -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.
  */
 
 /*
fs/xfs/scrub/trace.h +22 -0
index c4ebfb5..81becf6 100644
@@ -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
#33 lore raw

Re: [PATCH v2 10/21] xfs: set up scrub cross-referencing helpers

Quoted 8 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

---
v2: pass corruption flags directly to helpers, reducing helper function count

Ping?

--D

Quoted 461 lines
---
 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(-)
fs/xfs/scrub/btree.c +59 -12
index df07661..e17d154 100644
@@ -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;
 }
fs/xfs/scrub/btree.h +9 -0
index 4de825a6..e2b868e 100644
@@ -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,
fs/xfs/scrub/common.c +125 -10
index d5c37d8..35c47cf 100644
@@ -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;
+}
fs/xfs/scrub/common.h +16 -0
index fe12053..11a5bd0 100644
@@ -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,
fs/xfs/scrub/scrub.c +10 -0
index cd46077..0ed2a12 100644
@@ -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.
  */
 
 /*
fs/xfs/scrub/trace.h +22 -1
index c4ebfb5..81becf6 100644
@@ -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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Reviewed-by on v2 10/21
#34 lore raw

Re: [PATCH v2 10/21] xfs: set up scrub cross-referencing helpers

In reply to Darrick J. Wong
Quoted 6 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some helper functions that we'll use later to deal with problems we might encounter while cross referencing metadata with other metadata.

.....

fs/xfs/scrub/btree.c xfs_scrub_btree( · line 563

Quoted 7 lines
fs/xfs/scrub/btree.c +1 -0
@@ -512,5 +558,6 @@ xfs_scrub_btree(
 	}
 
 out:
+
 	return error;
 }

Stray whitespace.

Much nicer than the first version. :)

Signature · 2 lines
Dave Chinner
[email protected]
#35 lore raw

PATCH v11 11/21 xfs: fix a few erroneous process_error calls in the scrubbers

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
fs/xfs/scrub/dir.c +1 -1
index 69e1efd..e75826b 100644
@@ -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;
 
fs/xfs/scrub/inode.c +1 -1
index 61cd112..304e8bc 100644
@@ -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)
fs/xfs/scrub/parent.c +3 -3
index 63a2533..dd704fd 100644
@@ -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. */
Reviewed-by on v11 11/21
#36 lore raw

Re: [PATCH 11/21] xfs: fix a few erroneous process_error calls in the scrubbers

Quoted 11 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

looks good.

Signature · 2 lines
Dave Chinner
[email protected]
#37 lore raw

PATCH v11 12/21 xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/btree.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
fs/xfs/scrub/btree.c +92 -0
index 36cff8f..9151499 100644
@@ -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;
 }
#38 lore raw

Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree

Quoted 51 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/btree.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
fs/xfs/scrub/btree.c +28 -0
index 36cff8f..9151499 100644
@@ -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;

fs/xfs/scrub/btree.c xfs_scrub_btree_block_check_siblings( · line 402

Quoted 10 lines
fs/xfs/scrub/btree.c +10 -0
+		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.

Signature · 2 lines
Dave Chinner
[email protected]
#39 lore raw

Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree

Quoted 56 lines · Dave Chinner
Quoted 55 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/btree.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
fs/xfs/scrub/btree.c +32 -0
index 36cff8f..9151499 100644
@@ -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;
+	}
Quoted 8 lines

fs/xfs/scrub/btree.c xfs_scrub_btree_block_check_siblings( · line 402

Quoted 6 lines
fs/xfs/scrub/btree.c +6 -0
+
+	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

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reviewed-by
#40 lore raw

Re: [PATCH 12/21] xfs: check btree block ownership with bnobt/rmapbt when scrubbing btree

Quoted 66 lines · Darrick J. Wong
Quoted 56 lines · Dave Chinner
Quoted 55 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/btree.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
fs/xfs/scrub/btree.c +32 -0
index 36cff8f..9151499 100644
@@ -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;
+	}
Quoted 8 lines

fs/xfs/scrub/btree.c xfs_scrub_btree_block_check_siblings( · line 402

Quoted 6 lines
fs/xfs/scrub/btree.c +6 -0
+
+	if (psa == &sa)
+		xfs_scrub_ag_free(bs->sc, &sa);
+
+	return error;
+}

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

Quoted 7 lines

"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

Signature · 2 lines
Dave Chinner
[email protected]
#41 lore raw

PATCH v11 13/21 xfs: introduce scrubber cross-referencing stubs

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some stubs that will be used to cross-reference metadata records. The actual cross-referencing will be filled in by subsequent patches.

---
 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(-)
fs/xfs/scrub/agheader.c +64 -1
index 97beb47..5be9059 100644
@@ -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;
 }
fs/xfs/scrub/alloc.c +14 -0
index 059663e..0d95b84 100644
@@ -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;
 }
 
fs/xfs/scrub/bmap.c +28 -0
index 0261e11..d960b7a 100644
@@ -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;
 }
fs/xfs/scrub/ialloc.c +16 -1
index 496d6f2..599d62a 100644
@@ -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;
 }
fs/xfs/scrub/inode.c +13 -0
index 304e8bc..6cd6b34 100644
@@ -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);
fs/xfs/scrub/refcount.c +15 -0
index 2f88a8d..5a3aa9b 100644
@@ -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;
 }
 
fs/xfs/scrub/rmap.c +13 -0
index 97846c4..80edddb 100644
@@ -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;
 }
#42 lore raw

Re: [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs

Quoted 49 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some stubs that will be used to cross-reference metadata records. The actual cross-referencing will be filled in by subsequent patches.

---
 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(-)
fs/xfs/scrub/agheader.c +12 -1
index 97beb47..5be9059 100644
@@ -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.
  *

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 404

Quoted 8 lines
fs/xfs/scrub/agheader.c +5 -0
@@ -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.

Signature · 2 lines
Dave Chinner
[email protected]
#43 lore raw

Re: [PATCH 13/21] xfs: introduce scrubber cross-referencing stubs

Quoted 63 lines · Dave Chinner
Quoted 49 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some stubs that will be used to cross-reference metadata records. The actual cross-referencing will be filled in by subsequent patches.

---
 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(-)
fs/xfs/scrub/agheader.c +12 -1
index 97beb47..5be9059 100644
@@ -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.
  *

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 404

Quoted 8 lines
fs/xfs/scrub/agheader.c +5 -0
@@ -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?

Quoted 1 line

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

Done.

--D

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#44 lore raw

PATCH v2 13/21 xfs: introduce scrubber cross-referencing stubs

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some stubs that will be used to cross-reference metadata records. The actual cross-referencing will be filled in by subsequent patches.

---
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(-)
fs/xfs/scrub/agheader.c +62 -1
index 97beb47..1477aad 100644
@@ -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;
 }
fs/xfs/scrub/alloc.c +13 -0
index 059663e..03ed403 100644
@@ -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;
 }
 
fs/xfs/scrub/bmap.c +29 -0
index 0261e11..b693192 100644
@@ -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;
 }
fs/xfs/scrub/ialloc.c +15 -0
index 496d6f2..9294148 100644
@@ -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;
 }
 
fs/xfs/scrub/inode.c +12 -0
index 304e8bc..2aa12e1 100644
@@ -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);
fs/xfs/scrub/refcount.c +14 -0
index 2f88a8d..d202382 100644
@@ -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;
 }
 
fs/xfs/scrub/rmap.c +12 -0
index 97846c4..8655948 100644
@@ -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;
 }
Reviewed-by on v2 13/21
#45 lore raw

Re: [PATCH v2 13/21] xfs: introduce scrubber cross-referencing stubs

Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Create some stubs that will be used to cross-reference metadata records. The actual cross-referencing will be filled in by subsequent patches.

---
v2: move the OFLAG_CORRUPT checks into the helpers to clean up the call sites
---

looks good.

Signature · 2 lines
Dave Chinner
[email protected]
#46 lore raw

PATCH v11 14/21 xfs: cross-reference with the bnobt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
fs/xfs/scrub/agheader.c +84 -0
index 5be9059..3bb0f96 100644
@@ -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. */
fs/xfs/scrub/alloc.c +19 -0
index 0d95b84..3d6f8cc 100644
@@ -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);
+}
fs/xfs/scrub/bmap.c +20 -1
index d960b7a..a74771c 100644
@@ -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. */
fs/xfs/scrub/btree.c +12 -0
index 9151499..ae58fcc 100644
@@ -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);
 
fs/xfs/scrub/ialloc.c +1 -0
index 599d62a..4c4ef17c 100644
@@ -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? */
fs/xfs/scrub/inode.c +15 -0
index 6cd6b34..a00d179 100644
@@ -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. */
fs/xfs/scrub/refcount.c +1 -0
index 5a3aa9b..19c303d 100644
@@ -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. */
fs/xfs/scrub/rmap.c +4 -0
index 80edddb..5c9646b 100644
@@ -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. */
fs/xfs/scrub/scrub.h +5 -0
index 2a79614..e75ff0e 100644
@@ -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__ */
#47 lore raw

Re: [PATCH 14/21] xfs: cross-reference with the bnobt

Quoted 24 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
fs/xfs/scrub/agheader.c +0 -0
index 5be9059..3bb0f96 100644

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 115

Quoted 9 lines
fs/xfs/scrub/agheader.c +6 -0
@@ -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

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 121

Quoted 6 lines
fs/xfs/scrub/agheader.c +6 -0
+
+	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);
Quoted 24 lines
fs/xfs/scrub/agheader.c +14 -0
+	/* 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(

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 449

Quoted 10 lines
fs/xfs/scrub/agheader.c +8 -0
 	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)

Quoted 7 lines
fs/xfs/scrub/agheader.c +7 -0
+
+	error = xfs_scrub_ag_btcur_init(sc, &sc->sa);
+	if (error)
+		return;
+
+	xfs_scrub_xref_not_free(sc, &sc->sa.bno_cur, bno, 1);
+

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 466

Quoted 10 lines
fs/xfs/scrub/agheader.c +10 -0
+	/* 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?

Quoted 17 lines
fs/xfs/scrub/alloc.c +9 -0
index 0d95b84..3d6f8cc 100644
@@ -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)
+{

fs/xfs/scrub/alloc.c xfs_scrub_cntbt( · line 135

Quoted 10 lines
fs/xfs/scrub/alloc.c +10 -0
+	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...

Quoted 13 lines
fs/xfs/scrub/alloc.c +9 -0
@@ -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);

fs/xfs/scrub/alloc.c xfs_scrub_bmap_extent_xref( · line 138

Quoted 10 lines
fs/xfs/scrub/alloc.c +10 -0
+	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?

Quoted 8 lines
fs/xfs/scrub/alloc.c +0 -0
 }
 
 /* Scrub a single extent record. */
fs/xfs/scrub/btree.c +0 -0
index 9151499..ae58fcc 100644

fs/xfs/scrub/btree.c xfs_scrub_btree_check_block_owner( · line 389

Quoted 9 lines
fs/xfs/scrub/btree.c +3 -0
@@ -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.

Quoted 9 lines
fs/xfs/scrub/btree.c +5 -0
@@ -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;
+

fs/xfs/scrub/btree.c xfs_scrub_inode_xref( · line 601

Quoted 10 lines
fs/xfs/scrub/btree.c +10 -0
+	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.

Signature · 2 lines
Dave Chinner
[email protected]
#48 lore raw

Re: [PATCH 14/21] xfs: cross-reference with the bnobt

Quoted 20 lines · Dave Chinner
Quoted 19 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
Quoted 16 lines
Quoted 5 lines
fs/xfs/scrub/agheader.c +0 -0
index 5be9059..3bb0f96 100644

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 115

Quoted 9 lines
fs/xfs/scrub/agheader.c +6 -0
@@ -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).

Quoted 12 lines

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 121

Quoted 6 lines
fs/xfs/scrub/agheader.c +6 -0
+
+	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:

Quoted 1 line
	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. :)

Quoted 50 lines
Quoted 24 lines
fs/xfs/scrub/agheader.c +14 -0
+	/* 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(

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 449

Quoted 10 lines
fs/xfs/scrub/agheader.c +8 -0
 	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)

Quoted 13 lines
fs/xfs/scrub/agheader.c +13 -0
+
+	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);
Quoted 8 lines

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 466

Quoted 4 lines
fs/xfs/scrub/agheader.c +4 -0
+		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?

Quoted 52 lines
Quoted 19 lines
fs/xfs/scrub/alloc.c +9 -0
index 0d95b84..3d6f8cc 100644
@@ -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)
+{

fs/xfs/scrub/alloc.c xfs_scrub_cntbt( · line 135

Quoted 10 lines
fs/xfs/scrub/alloc.c +10 -0
+	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...

Quoted 19 lines
fs/xfs/scrub/alloc.c +15 -0
@@ -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;
Quoted 8 lines

fs/xfs/scrub/alloc.c xfs_scrub_bmap_extent_xref( · line 138

Quoted 4 lines
fs/xfs/scrub/alloc.c +4 -0
+
+	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

Quoted 52 lines
Quoted 8 lines
fs/xfs/scrub/alloc.c +0 -0
 }
 
 /* Scrub a single extent record. */
fs/xfs/scrub/btree.c +0 -0
index 9151499..ae58fcc 100644

fs/xfs/scrub/btree.c xfs_scrub_btree_check_block_owner( · line 389

Quoted 9 lines
fs/xfs/scrub/btree.c +3 -0
@@ -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.

Quoted 9 lines
fs/xfs/scrub/btree.c +5 -0
@@ -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;
+

fs/xfs/scrub/btree.c xfs_scrub_inode_xref( · line 601

Quoted 10 lines
fs/xfs/scrub/btree.c +10 -0
+	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.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#49 lore raw

Re: [PATCH 14/21] xfs: cross-reference with the bnobt

Quoted 23 lines · Darrick J. Wong
Quoted 13 lines · Dave Chinner
Quoted 12 lines · Darrick J. Wong
fs/xfs/scrub/agheader.c +12 -0
+	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);
Quoted 8 lines

fs/xfs/scrub/agheader.c xfs_scrub_superblock( · line 466

Quoted 4 lines
fs/xfs/scrub/agheader.c +4 -0
+		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?

Quoted 7 lines

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.

Signature · 2 lines
Dave Chinner
[email protected]
#50 lore raw

PATCH v2 14/21 xfs: cross-reference with the bnobt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
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(+)
fs/xfs/scrub/agheader.c +87 -0
index 1477aad..58e544b 100644
@@ -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. */
fs/xfs/scrub/alloc.c +19 -0
index 03ed403..564c76b 100644
@@ -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);
+}
fs/xfs/scrub/bmap.c +19 -0
index b693192..7e8e239 100644
@@ -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. */
fs/xfs/scrub/btree.c +5 -0
index 3bb88d8..3dbac4c 100644
@@ -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);
 
fs/xfs/scrub/ialloc.c +2 -0
index 9294148..4526894 100644
@@ -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? */
fs/xfs/scrub/inode.c +15 -0
index 2aa12e1..be9cf19 100644
@@ -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. */
fs/xfs/scrub/refcount.c +2 -0
index 4c550b3..09a04ae 100644
@@ -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. */
fs/xfs/scrub/rmap.c +5 -0
index 8655948..54b0eac 100644
@@ -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. */
fs/xfs/scrub/scrub.h +4 -0
index 2a79614..006e396 100644
@@ -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__ */
Reviewed-by on v2 14/21
#51 lore raw

Re: [PATCH v2 14/21] xfs: cross-reference with the bnobt

Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

Looks much better.

Signature · 2 lines
Dave Chinner
[email protected]
#52 lore raw

PATCH v11 15/21 xfs: cross-reference bnobt records with cntbt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Scrub should make sure that each bnobt record has a corresponding cntbt record.

---
 fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)
fs/xfs/scrub/agheader.c +26 -0
index 3bb0f96..e87022f 100644
@@ -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 */
 }
 
fs/xfs/scrub/alloc.c +37 -0
index 3d6f8cc..9a28e3d 100644
@@ -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. */
#53 lore raw

Re: [PATCH 15/21] xfs: cross-reference bnobt records with cntbt

Quoted 29 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Scrub should make sure that each bnobt record has a corresponding cntbt record.

---
 fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)
fs/xfs/scrub/agheader.c +2 -0
index 3bb0f96..e87022f 100644
@@ -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. */

fs/xfs/scrub/agheader.c xfs_scrub_agf_xref( · line 479

Quoted 10 lines
fs/xfs/scrub/agheader.c +10 -0
+	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.

fs/xfs/scrub/agheader.c xfs_scrub_agf_xref( · line 492

Quoted 13 lines
fs/xfs/scrub/agheader.c +13 -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?

Quoted 46 lines
fs/xfs/scrub/agheader.c +1 -0
+
 	/* scrub teardown will take care of sc->sa for us */
 }
 
fs/xfs/scrub/alloc.c +27 -0
index 3d6f8cc..9a28e3d 100644
@@ -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))

fs/xfs/scrub/alloc.c xfs_scrub_allocbt_xref( · line 96

Quoted 10 lines
fs/xfs/scrub/alloc.c +10 -0
+			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.

Signature · 2 lines
Dave Chinner
[email protected]
#54 lore raw

Re: [PATCH 15/21] xfs: cross-reference bnobt records with cntbt

Quoted 29 lines · Dave Chinner
Quoted 28 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Scrub should make sure that each bnobt record has a corresponding cntbt record.

---
 fs/xfs/scrub/agheader.c |   26 ++++++++++++++++++++++++++
 fs/xfs/scrub/alloc.c    |   37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)
fs/xfs/scrub/agheader.c +1 -0
index 3bb0f96..e87022f 100644
@@ -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);
 	}
 
Quoted 13 lines

fs/xfs/scrub/agheader.c xfs_scrub_agf_xref( · line 479

Quoted 11 lines
fs/xfs/scrub/agheader.c +11 -0
+	/* 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.

Quoted 8 lines
Quoted 8 lines
fs/xfs/scrub/agheader.c +8 -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))
Quoted 8 lines

fs/xfs/scrub/agheader.c xfs_scrub_agf_xref( · line 492

Quoted 5 lines
fs/xfs/scrub/agheader.c +5 -0
+			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

Quoted 69 lines
Quoted 46 lines
fs/xfs/scrub/agheader.c +1 -0
+
 	/* scrub teardown will take care of sc->sa for us */
 }
 
fs/xfs/scrub/alloc.c +27 -0
index 3d6f8cc..9a28e3d 100644
@@ -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))

fs/xfs/scrub/alloc.c xfs_scrub_allocbt_xref( · line 96

Quoted 10 lines
fs/xfs/scrub/alloc.c +10 -0
+			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.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#55 lore raw

PATCH v2 15/21 xfs: cross-reference bnobt records with cntbt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Scrub should make sure that each bnobt record has a corresponding cntbt record.

---
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(+)
fs/xfs/scrub/agheader.c +35 -0
index 58e544b..3b66cd4 100644
@@ -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 */
 }
 
fs/xfs/scrub/alloc.c +45 -0

63 lines not on this page · raw message

Reviewed-by on v2 15/21
#56 lore raw

Re: [PATCH v2 15/21] xfs: cross-reference bnobt records with cntbt

Quoted 5 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Scrub should make sure that each bnobt record has a corresponding cntbt record.

Quoted 7 lines

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

Signature · 2 lines
Dave Chinner
[email protected]
#57 lore raw

PATCH v11 16/21 xfs: cross-reference inode btrees during scrub

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
 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(+)
fs/xfs/scrub/agheader.c +25 -0
index e87022f..9b78218 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +3 -0
index 9a28e3d..cd709f4 100644
@@ -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. */
fs/xfs/scrub/bmap.c +2 -0
index a74771c..df84c58 100644
@@ -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);
 }
fs/xfs/scrub/ialloc.c +63 -0

73 lines not on this page · raw message

fs/xfs/scrub/inode.c +2 -0
index a00d179..766d9ac 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +2 -0
index 19c303d..8add281 100644
@@ -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. */
fs/xfs/scrub/rmap.c +6 -0
index 5c9646b..8d49556 100644
@@ -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. */
fs/xfs/scrub/scrub.h +6 -0
index e75ff0e..768df35 100644
@@ -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__ */
#58 lore raw

PATCH v2 16/21 xfs: cross-reference inode btrees during scrub

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
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(+)
fs/xfs/scrub/agheader.c +19 -0
index 3b66cd4..4d4ce1f 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index 4101560..2a58558 100644
@@ -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. */
fs/xfs/scrub/bmap.c +1 -0
index 7e8e239..d2b4747 100644
@@ -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);
 }
fs/xfs/scrub/ialloc.c +70 -0

83 lines not on this page · raw message

fs/xfs/scrub/inode.c +49 -0

67 lines not on this page · raw message

fs/xfs/scrub/refcount.c +1 -0
index 09a04ae..700f8f1 100644
@@ -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. */
fs/xfs/scrub/rmap.c +4 -0
index 54b0eac..02704b0 100644
@@ -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. */
fs/xfs/scrub/scrub.h +4 -0
index 006e396..340273f 100644
@@ -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__ */
#59 lore raw

Re: [PATCH v2 16/21] xfs: cross-reference inode btrees during scrub

Quoted 24 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
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(+)
fs/xfs/scrub/agheader.c +0 -0
index 3b66cd4..4d4ce1f 100644

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 125

Quoted 4 lines
fs/xfs/scrub/agheader.c +1 -0
@@ -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"....

fs/xfs/scrub/agheader.c xfs_scrub_agi_xref( · line 763

Quoted 5 lines
fs/xfs/scrub/agheader.c +1 -0
@@ -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

fs/xfs/scrub/agheader.c xfs_scrub_agi_xref( · line 773

Quoted 8 lines
fs/xfs/scrub/agheader.c +8 -0
+	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;

?

Quoted 24 lines
fs/xfs/scrub/ialloc.c +12 -0
index 4526894..34c133e 100644
@@ -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;

fs/xfs/scrub/ialloc.c xfs_scrub_iallocbt_chunk_xref( · line 95

Quoted 10 lines
fs/xfs/scrub/ialloc.c +10 -0
+	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?

Quoted 45 lines
fs/xfs/scrub/ialloc.c +38 -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;

fs/xfs/scrub/ialloc.c xfs_scrub_finobt( · line 424

Quoted 10 lines
fs/xfs/scrub/ialloc.c +10 -0
+
+	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.

Signature · 2 lines
Dave Chinner
[email protected]
#60 lore raw

Re: [PATCH v2 16/21] xfs: cross-reference inode btrees during scrub

Quoted 27 lines · Dave Chinner
Quoted 26 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
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(+)
fs/xfs/scrub/agheader.c +0 -0
index 3b66cd4..4d4ce1f 100644
@@ -122,6 +122,7 @@ xfs_scrub_superblock_xref(
 		return;
 
Quoted 5 lines

fs/xfs/scrub/agheader.c xfs_scrub_superblock_xref( · line 125

Quoted 2 lines
fs/xfs/scrub/agheader.c +1 -0
 	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. :)

Quoted 8 lines

fs/xfs/scrub/agheader.c xfs_scrub_agi_xref( · line 763

Quoted 5 lines
fs/xfs/scrub/agheader.c +1 -0
@@ -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>

Quoted 11 lines

fs/xfs/scrub/agheader.c xfs_scrub_agi_xref( · line 773

Quoted 8 lines
fs/xfs/scrub/agheader.c +8 -0
+	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:

Quoted 4 lines

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.

Quoted 28 lines
Quoted 28 lines
fs/xfs/scrub/ialloc.c +16 -0
index 4526894..34c133e 100644
@@ -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,
Quoted 8 lines

fs/xfs/scrub/ialloc.c xfs_scrub_iallocbt_chunk_xref( · line 95

Quoted 6 lines
fs/xfs/scrub/ialloc.c +6 -0
+				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>

Quoted 70 lines
Quoted 45 lines
fs/xfs/scrub/ialloc.c +38 -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;

fs/xfs/scrub/ialloc.c xfs_scrub_finobt( · line 424

Quoted 10 lines
fs/xfs/scrub/ialloc.c +10 -0
+
+	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);
}
Quoted 6 lines

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>

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#61 lore raw

PATCH v3 16/21 xfs: cross-reference inode btrees during scrub

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
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(+)
fs/xfs/scrub/agheader.c +27 -0
index 1aba7c0..13ec76b 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index 836b3c3..0031014 100644
@@ -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. */
fs/xfs/scrub/bmap.c +1 -0
index 7e8e239..6f1d145 100644
@@ -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);
 }
fs/xfs/scrub/ialloc.c +73 -0

88 lines not on this page · raw message

fs/xfs/scrub/inode.c +49 -0

67 lines not on this page · raw message

fs/xfs/scrub/refcount.c +1 -0
index 09a04ae..af54590 100644
@@ -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. */
fs/xfs/scrub/rmap.c +4 -0
index 54b0eac..6e937ef 100644
@@ -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. */
fs/xfs/scrub/scrub.h +4 -0
index cbc6363..9b0033b 100644
@@ -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__ */
Reviewed-by on v3 16/21
#62 lore raw

Re: [PATCH v3 16/21] xfs: cross-reference inode btrees during scrub

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Cross-reference the inode btrees with the other metadata when we scrub the filesystem.

---
v3: fix some indenting craziness, clean up helpers per dave's suggestion
v2: streamline scrubber arguments, remove stack allocated objects
---

Looks good.

Signature · 2 lines
Dave Chinner
[email protected]
#63 lore raw

PATCH v11 17/21 xfs: cross-reference reverse-mapping btree

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 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(-)
fs/xfs/scrub/agheader.c +68 -2
index 9b78218..c3d244d 100644
@@ -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 */
 }
 
fs/xfs/scrub/alloc.c +1 -0
index cd709f4..a4046c1 100644
@@ -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. */
fs/xfs/scrub/bmap.c +102 -0

114 lines not on this page · raw message

fs/xfs/scrub/btree.c +2 -0
index ae58fcc..2336c84 100644
@@ -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);
 
fs/xfs/scrub/common.c +47 -0

53 lines not on this page · raw message

fs/xfs/scrub/common.h +4 -0
index d7ee72a..519f6e8 100644
@@ -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);
fs/xfs/scrub/ialloc.c +76 -1

122 lines not on this page · raw message

fs/xfs/scrub/inode.c +4 -0
index 766d9ac..8fb2aac 100644
@@ -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);
 }
fs/xfs/scrub/rmap.c +64 -0

67 lines not on this page · raw message

fs/xfs/scrub/scrub.h +9 -0
index 768df35..4789dba 100644
@@ -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__ */
#64 lore raw

PATCH v2 17/21 xfs: cross-reference reverse-mapping btree

From: Darrick J. Wong <[email protected]>

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.

---
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(-)
fs/xfs/scrub/agheader.c +66 -2
index 4d4ce1f..6bc8121 100644
@@ -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 */
 }
 
fs/xfs/scrub/alloc.c +1 -0
index 2a58558..b4defa4 100644
@@ -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. */
fs/xfs/scrub/bmap.c +104 -0

116 lines not on this page · raw message

fs/xfs/scrub/btree.c +1 -0
index 3dbac4c..1bdfa99 100644
@@ -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);
fs/xfs/scrub/common.c +47 -0

53 lines not on this page · raw message

fs/xfs/scrub/common.h +4 -0
index 84c302f..bf88a67 100644
@@ -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);
fs/xfs/scrub/ialloc.c +75 -1

121 lines not on this page · raw message

fs/xfs/scrub/inode.c +4 -0
index b48d5fb..fc546f2 100644
@@ -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);
 }
fs/xfs/scrub/rmap.c +63 -0

66 lines not on this page · raw message

fs/xfs/scrub/scrub.h +8 -0
index 340273f..53cc526 100644
@@ -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__ */
#65 lore raw

Re: [PATCH v2 17/21] xfs: cross-reference reverse-mapping btree

Quoted 11 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

.....

fs/xfs/scrub/alloc.c xfs_scrub_allocbt_xref( · line 108

Quoted 9 lines
fs/xfs/scrub/alloc.c +1 -0
index 2a58558..b4defa4 100644
@@ -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()?

Quoted 29 lines
fs/xfs/scrub/bmap.c +21 -0
index d2b4747..ef7b461 100644
@@ -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 {

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 132

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+		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?

Quoted 23 lines
fs/xfs/scrub/bmap.c +23 -0
+		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,

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 166

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+				&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....

Quoted 12 lines
fs/xfs/scrub/bmap.c +12 -0
+
+	/* 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 ||

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 188

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+		    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?

.....

Quoted 9 lines
fs/xfs/scrub/common.c +1 -0
index 68fea09..d3aaa6a 100644
@@ -325,6 +325,53 @@ xfs_scrub_set_incomplete(
 }
 
 /*
+ * rmap scrubbing -- compute the number of blocks with a given owner,

fs/xfs/scrub/common.c xfs_scrub_set_incomplete( · line 338

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+ * 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()

fs/xfs/scrub/common.c xfs_scrub_set_incomplete( · line 348

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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;

?

Quoted 11 lines
fs/xfs/scrub/common.c +3 -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;
 		}
 

fs/xfs/scrub/common.c xfs_scrub_iallocbt_check_freemask( · line 244

Quoted 10 lines
fs/xfs/scrub/common.c +7 -0
+		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?

Quoted 9 lines
fs/xfs/scrub/common.c +1 -0
@@ -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;

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 318

Quoted 5 lines
fs/xfs/scrub/common.c +2 -0
@@ -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?

Quoted 20 lines
fs/xfs/scrub/common.c +13 -0
 	/* 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;

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 386

Quoted 10 lines
fs/xfs/scrub/common.c +10 -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?

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 390

Quoted 4 lines
fs/xfs/scrub/common.c +4 -0
+
+	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.

.....

Quoted 7 lines
fs/xfs/scrub/common.c +2 -0
@@ -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;
 

fs/xfs/scrub/common.c xfs_scrub_iallocbt( · line 433

Quoted 10 lines
fs/xfs/scrub/common.c +7 -1
 	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?

.....

Quoted 7 lines
fs/xfs/scrub/rmap.c +0 -0
index 02704b0..8421c6e 100644
@@ -157,3 +157,66 @@ xfs_scrub_rmapbt(
 	return xfs_scrub_btree(sc, sc->sa.rmap_cur, xfs_scrub_rmapbt_rec,
 			&oinfo, NULL);

fs/xfs/scrub/rmap.c xfs_scrub_rmapbt( · line 168

Quoted 10 lines
fs/xfs/scrub/rmap.c +9 -0
 }
+
+/* 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.

Signature · 2 lines
Dave Chinner
[email protected]
#66 lore raw

Re: [PATCH v2 17/21] xfs: cross-reference reverse-mapping btree

Quoted 21 lines · Dave Chinner
Quoted 11 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

.....

Quoted 6 lines
fs/xfs/scrub/alloc.c +0 -0
index 2a58558..b4defa4 100644
@@ -105,6 +105,7 @@ xfs_scrub_allocbt_xref(
 
Quoted 9 lines

fs/xfs/scrub/alloc.c xfs_scrub_allocbt_xref( · line 108

Quoted 3 lines
fs/xfs/scrub/alloc.c +1 -0
 	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.

Quoted 33 lines
Quoted 33 lines
fs/xfs/scrub/bmap.c +25 -0
index d2b4747..ef7b461 100644
@@ -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;
+	}
+
Quoted 8 lines

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 133

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+	/* 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.

Quoted 55 lines
Quoted 23 lines
fs/xfs/scrub/bmap.c +23 -0
+		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,

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 166

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+				&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....

Quoted 18 lines
fs/xfs/scrub/bmap.c +18 -0
+
+	/* 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
Quoted 8 lines

fs/xfs/scrub/bmap.c struct xfs_scrub_bmap_info { · line 188

Quoted 4 lines
fs/xfs/scrub/bmap.c +4 -0
+		 * 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.

Quoted 9 lines

.....

Quoted 7 lines
fs/xfs/scrub/common.c +0 -0
index 68fea09..d3aaa6a 100644
@@ -325,6 +325,53 @@ xfs_scrub_set_incomplete(
 }
 
Quoted 14 lines

fs/xfs/scrub/common.c xfs_scrub_set_incomplete( · line 338

Quoted 12 lines
fs/xfs/scrub/common.c +11 -0
 /*
+ * 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.

Quoted 23 lines

fs/xfs/scrub/common.c xfs_scrub_set_incomplete( · line 348

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+	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;
Quoted 1 line

?

Yep.

Quoted 11 lines
Quoted 11 lines
fs/xfs/scrub/common.c +3 -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;
 		}
 
Quoted 13 lines

fs/xfs/scrub/common.c xfs_scrub_iallocbt_check_freemask( · line 244

Quoted 10 lines
fs/xfs/scrub/common.c +7 -0
+		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.

Quoted 9 lines
Quoted 9 lines
fs/xfs/scrub/common.c +1 -0
@@ -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(
Quoted 8 lines

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 318

Quoted 5 lines
fs/xfs/scrub/common.c +2 -0
 	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.

Quoted 25 lines
Quoted 25 lines
fs/xfs/scrub/common.c +18 -0
 	/* 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;
Quoted 8 lines

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 386

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	/* 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.

Quoted 6 lines

fs/xfs/scrub/common.c xfs_scrub_iallocbt_rec( · line 390

Quoted 4 lines
fs/xfs/scrub/common.c +4 -0
+
+	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.

Quoted 8 lines

.....

Quoted 7 lines
fs/xfs/scrub/common.c +2 -0
@@ -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;
 
Quoted 12 lines

fs/xfs/scrub/common.c xfs_scrub_iallocbt( · line 433

Quoted 10 lines
fs/xfs/scrub/common.c +7 -1
 	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.

Quoted 12 lines

.....

Quoted 11 lines
fs/xfs/scrub/rmap.c +3 -0
index 02704b0..8421c6e 100644
@@ -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
Quoted 8 lines

fs/xfs/scrub/rmap.c xfs_scrub_rmapbt( · line 168

Quoted 6 lines
fs/xfs/scrub/rmap.c +6 -0
+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

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#67 lore raw

PATCH v3 17/21 xfs: cross-reference reverse-mapping btree

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
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(-)
fs/xfs/scrub/agheader.c +66 -2
index 13ec76b..1d109d5 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index 0031014..3faa437 100644
@@ -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. */
fs/xfs/scrub/bmap.c +134 -0

146 lines not on this page · raw message

fs/xfs/scrub/btree.c +4 -0
index 222e031..0589d4e 100644
@@ -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);
 
fs/xfs/scrub/common.c +53 -0

59 lines not on this page · raw message

fs/xfs/scrub/common.h +4 -0
index 84c302f..bf88a67 100644
@@ -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);
fs/xfs/scrub/ialloc.c +101 -2

148 lines not on this page · raw message

fs/xfs/scrub/inode.c +4 -0
index b48d5fb..6e99577 100644
@@ -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);
 }
fs/xfs/scrub/rmap.c +65 -0

68 lines not on this page · raw message

fs/xfs/scrub/scrub.h +8 -0
index 9b0033b..8fcf491 100644
@@ -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__ */
Reviewed-by on v3 17/21
#68 lore raw

Re: [PATCH v3 17/21] xfs: cross-reference reverse-mapping btree

Quoted 7 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

Quoted 5 lines

---
v3: fix indenting and standardize helper naming, refactor helpers per dave's suggestion
v2: streamline scrubber arguments, remove stack allocated objects
---

Looks ok.

Signature · 2 lines
Dave Chinner
[email protected]
#69 lore raw

PATCH v11 18/21 xfs: cross-reference the rmapbt data with the refcountbt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +316 -2
index 8add281..b9764f5 100644
@@ -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;
 }
#70 lore raw

PATCH v2 18/21 xfs: cross-reference the rmapbt data with the refcountbt

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +316 -2
index 700f8f1..df18e47 100644
@@ -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;
 }
#71 lore raw

Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt

Quoted 46 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +24 -0
index 700f8f1..df18e47 100644
@@ -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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 86

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+ *    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....

.....

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 153

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+	} 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....

Quoted 35 lines
fs/xfs/scrub/refcount.c +35 -0
+	}
+
+	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

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 198

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+	 * 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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 201

Quoted 3 lines
fs/xfs/scrub/refcount.c +3 -0
+		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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 147

Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+		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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 214

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+		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....

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 220

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+
+	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....

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 224

Quoted 4 lines
fs/xfs/scrub/refcount.c +4 -0
+			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.

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+				continue;
+			}
+			list_del(&frag->list);
+			kmem_free(frag);
+			nr++;
+		}

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 240

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+
+		/* 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

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 248

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -0
+			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?

Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+		rbno = next_rbno;
+	}

.....

Quoted 19 lines
fs/xfs/scrub/refcount.c +19 -0
+/* 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;

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 302

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+	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)?

....

Quoted 21 lines
fs/xfs/scrub/refcount.c +21 -0
+/* 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);
+

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 408

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -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....

Quoted 11 lines
fs/xfs/scrub/refcount.c +4 -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;
 

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 427

Quoted 10 lines
fs/xfs/scrub/refcount.c +7 -2
 	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.

fs/xfs/scrub/refcount.c

Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+
+	return error;

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

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#72 lore raw

Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt

Quoted 54 lines · Dave Chinner
Quoted 53 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +31 -0
index 700f8f1..df18e47 100644
@@ -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.
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 86

Quoted 3 lines
fs/xfs/scrub/refcount.c +3 -0
+ *
+ * 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.

Quoted 74 lines

.....

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 153

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+	} 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....

Quoted 35 lines
fs/xfs/scrub/refcount.c +35 -0
+	}
+
+	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

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 198

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+	 * 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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 201

Quoted 3 lines
fs/xfs/scrub/refcount.c +3 -0
+		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

Quoted 4 lines
		if (bno < rbno)
			rbno = bno;

Makes a lot more sense to me.

Ok.

Quoted 8 lines

fs/xfs/scrub/refcount.c

Quoted 3 lines
fs/xfs/scrub/refcount.c +2 -0
+		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>

Quoted 14 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 214

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+		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....

Quoted 10 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 220

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+
+	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... */

Quoted 7 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 224

Quoted 5 lines
fs/xfs/scrub/refcount.c +4 -0
+			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>

Quoted 19 lines
Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+				continue;
+			}
+			list_del(&frag->list);
+			kmem_free(frag);
+			nr++;
+		}

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 240

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+
+		/* 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

Quoted 13 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 248

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -0
+			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.

Quoted 28 lines
Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+		rbno = next_rbno;
+	}

.....

Quoted 24 lines
fs/xfs/scrub/refcount.c +24 -0
+/* 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;
+
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 302

Quoted 5 lines
fs/xfs/scrub/refcount.c +5 -0
+	/* 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.

Quoted 28 lines

....

Quoted 26 lines
fs/xfs/scrub/refcount.c +26 -0
+/* 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);
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 408

Quoted 5 lines
fs/xfs/scrub/refcount.c +5 -0
+	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.

Quoted 11 lines
Quoted 11 lines
fs/xfs/scrub/refcount.c +4 -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;
Quoted 13 lines

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 427

Quoted 10 lines
fs/xfs/scrub/refcount.c +7 -2
 	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>

Quoted 4 lines

fs/xfs/scrub/refcount.c

Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+
+	return error;

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

Ok.

--D

Quoted 12 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#73 lore raw

Re: [PATCH v2 18/21] xfs: cross-reference the rmapbt data with the refcountbt

Quoted 64 lines · Darrick J. Wong
Quoted 54 lines · Dave Chinner
Quoted 53 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/refcount.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +31 -0
index 700f8f1..df18e47 100644
@@ -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.
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 86

Quoted 3 lines
fs/xfs/scrub/refcount.c +3 -0
+ *
+ * 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....

Quoted 6 lines

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.

Quoted 117 lines
Quoted 74 lines

.....

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 153

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+	} 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....

Quoted 35 lines
fs/xfs/scrub/refcount.c +35 -0
+	}
+
+	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

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 198

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+	 * 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.

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 201

Quoted 3 lines
fs/xfs/scrub/refcount.c +3 -0
+		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

Quoted 4 lines
		if (bno < rbno)
			rbno = bno;

Makes a lot more sense to me.

Ok.

Quoted 8 lines

fs/xfs/scrub/refcount.c

Quoted 3 lines
fs/xfs/scrub/refcount.c +2 -0
+		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>

Quoted 14 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 214

Quoted 11 lines
fs/xfs/scrub/refcount.c +11 -0
+		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....

Quoted 10 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 220

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+
+	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....

Quoted 1 line

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

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

--D

Quoted 185 lines
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 224

Quoted 6 lines
fs/xfs/scrub/refcount.c +4 -0
+			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>

Quoted 19 lines
Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+				continue;
+			}
+			list_del(&frag->list);
+			kmem_free(frag);
+			nr++;
+		}

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 240

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+
+		/* 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

Quoted 13 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 248

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -0
+			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.

Quoted 28 lines
Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+		rbno = next_rbno;
+	}

.....

Quoted 24 lines
fs/xfs/scrub/refcount.c +24 -0
+/* 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;
+
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 302

Quoted 5 lines
fs/xfs/scrub/refcount.c +5 -0
+	/* 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.

Quoted 28 lines

....

Quoted 26 lines
fs/xfs/scrub/refcount.c +26 -0
+/* 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);
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 408

Quoted 5 lines
fs/xfs/scrub/refcount.c +5 -0
+	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.

Quoted 11 lines
Quoted 11 lines
fs/xfs/scrub/refcount.c +4 -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;
Quoted 13 lines

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt_rec( · line 427

Quoted 10 lines
fs/xfs/scrub/refcount.c +7 -2
 	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>

Quoted 4 lines

fs/xfs/scrub/refcount.c

Quoted 2 lines
fs/xfs/scrub/refcount.c +2 -0
+
+	return error;

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

Ok.

--D

Quoted 12 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#74 lore raw

PATCH v3 18/21 xfs: cross-reference the rmapbt data with the refcountbt

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

---
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(-)
fs/xfs/scrub/refcount.c +338 -2
index af54590..27e00fa 100644
@@ -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;
 }
Reviewed-by on v3 18/21
#75 lore raw

Re: [PATCH v3 18/21] xfs: cross-reference the rmapbt data with the refcountbt

Quoted 7 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

Quoted 8 lines

---
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:

Quoted 9 lines
fs/xfs/scrub/refcount.c +9 -0
+	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;

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 247

Quoted 10 lines
fs/xfs/scrub/refcount.c +10 -0
+				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 :)

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#76 lore raw

Re: [PATCH v3 18/21] xfs: cross-reference the rmapbt data with the refcountbt

Quoted 33 lines · Dave Chinner
Quoted 7 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

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.

Quoted 8 lines

---
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:

Quoted 14 lines
fs/xfs/scrub/refcount.c +14 -0
+	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++;
Quoted 8 lines

fs/xfs/scrub/refcount.c xfs_scrub_setup_ag_refcountbt( · line 247

Quoted 5 lines
fs/xfs/scrub/refcount.c +5 -0
+		}
+
+		/* 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.

Quoted 5 lines

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

Hooray! Thanks for the review!

--D

Quoted 11 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#77 lore raw

PATCH v11 19/21 xfs: cross-reference refcount btree during scrub

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
 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(+)
fs/xfs/scrub/agheader.c +14 -0
index c3d244d..e36ea25 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index a4046c1..d1dd9b7 100644
@@ -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. */
fs/xfs/scrub/bmap.c +79 -0

97 lines not on this page · raw message

fs/xfs/scrub/ialloc.c +1 -0
index accec93..8df1695 100644
@@ -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? */
fs/xfs/scrub/inode.c +1 -0
index 8fb2aac..b32a01d 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +67 -0

76 lines not on this page · raw message

fs/xfs/scrub/rmap.c +40 -0
index 7970e73..fee31e9 100644
@@ -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. */
fs/xfs/scrub/scrub.h +6 -0
index 4789dba..83cdcd8 100644
@@ -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__ */
#78 lore raw

PATCH v2 19/21 xfs: cross-reference refcount btree during scrub

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
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(+)
fs/xfs/scrub/agheader.c +13 -0
index 6bc8121..808ef3c 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index b4defa4..3f5168f 100644
@@ -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. */
fs/xfs/scrub/bmap.c +77 -0

95 lines not on this page · raw message

fs/xfs/scrub/ialloc.c +1 -0
index 30603b4..fb16314 100644
@@ -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? */
fs/xfs/scrub/inode.c +1 -0
index fc546f2..f12586c 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +65 -0

74 lines not on this page · raw message

fs/xfs/scrub/rmap.c +38 -0
index 8421c6e..e217f98 100644
@@ -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. */
fs/xfs/scrub/scrub.h +4 -0
index 53cc526..06c2d17 100644
@@ -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__ */
#79 lore raw

Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub

Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

....

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 211

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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)
Quoted 8 lines
fs/xfs/scrub/bmap.c +8 -0
+{
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
+	int				error;
+
+	if (!info->sc->sa.refc_cur)
+		return;
+

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 229

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+	/* 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?

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 237

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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

Quoted 16 lines
fs/xfs/scrub/bmap.c +16 -0
+{
+	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)

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 263

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -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

Quoted 14 lines
fs/xfs/scrub/bmap.c +7 -0
+{
+	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);

fs/xfs/scrub/bmap.c xfs_scrub_bmap_extent_xref( · line 321

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+	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

Quoted 41 lines
fs/xfs/scrub/bmap.c +1 -0
+	}
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
fs/xfs/scrub/ialloc.c +1 -0
index 30603b4..fb16314 100644
@@ -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? */
fs/xfs/scrub/inode.c +1 -0
index fc546f2..f12586c 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +1 -0
index df18e47..6e5b12a 100644
@@ -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"

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 437

Quoted 9 lines
fs/xfs/scrub/refcount.c +6 -0
@@ -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

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+	xfs_extlen_t			len)
+{
+	struct xfs_refcount_irec	rc;
+	bool				has_cowflag;
+	int				has_refcount;
+	int				error;

.....

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 483

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -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,

agbno

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#80 lore raw

Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub

Quoted 23 lines · Dave Chinner
Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

....

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 211

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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?

Quoted 4 lines

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

Quoted 14 lines
Quoted 14 lines
fs/xfs/scrub/bmap.c +13 -0
+{
+	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))
Quoted 8 lines

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 229

Quoted 5 lines
fs/xfs/scrub/bmap.c +5 -0
+		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

Quoted 146 lines

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 237

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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

Quoted 16 lines
fs/xfs/scrub/bmap.c +16 -0
+{
+	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)

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 263

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -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

Quoted 14 lines
fs/xfs/scrub/bmap.c +7 -0
+{
+	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);

fs/xfs/scrub/bmap.c xfs_scrub_bmap_extent_xref( · line 321

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+	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

Quoted 41 lines
fs/xfs/scrub/bmap.c +1 -0
+	}
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
fs/xfs/scrub/ialloc.c +1 -0
index 30603b4..fb16314 100644
@@ -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? */
fs/xfs/scrub/inode.c +1 -0
index fc546f2..f12586c 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +1 -0
index df18e47..6e5b12a 100644
@@ -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"

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 437

Quoted 9 lines
fs/xfs/scrub/refcount.c +6 -0
@@ -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

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+	xfs_extlen_t			len)
+{
+	struct xfs_refcount_irec	rc;
+	bool				has_cowflag;
+	int				has_refcount;
+	int				error;

.....

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 483

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -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,

agbno

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#81 lore raw

Re: [PATCH v2 19/21] xfs: cross-reference refcount btree during scrub

Quoted 53 lines · Darrick J. Wong
Quoted 23 lines · Dave Chinner
Quoted 9 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
v2: streamline scrubber arguments, remove stack allocated objects
---

....

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 211

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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?

Quoted 4 lines

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

Quoted 14 lines
Quoted 14 lines
fs/xfs/scrub/bmap.c +13 -0
+{
+	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))
Quoted 8 lines

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 229

Quoted 5 lines
fs/xfs/scrub/bmap.c +5 -0
+		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?

Quoted 3 lines

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

Quoted 153 lines

--D

Quoted 136 lines

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 237

Quoted 6 lines
fs/xfs/scrub/bmap.c +6 -0
+/* 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

Quoted 16 lines
fs/xfs/scrub/bmap.c +16 -0
+{
+	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)

fs/xfs/scrub/bmap.c xfs_scrub_bmap_xref_rmap( · line 263

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -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

Quoted 14 lines
fs/xfs/scrub/bmap.c +7 -0
+{
+	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);

fs/xfs/scrub/bmap.c xfs_scrub_bmap_extent_xref( · line 321

Quoted 10 lines
fs/xfs/scrub/bmap.c +10 -0
+	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

Quoted 41 lines
fs/xfs/scrub/bmap.c +1 -0
+	}
 
 	xfs_scrub_ag_free(info->sc, &info->sc->sa);
 }
fs/xfs/scrub/ialloc.c +1 -0
index 30603b4..fb16314 100644
@@ -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? */
fs/xfs/scrub/inode.c +1 -0
index fc546f2..f12586c 100644
@@ -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);
 }
fs/xfs/scrub/refcount.c +1 -0
index df18e47..6e5b12a 100644
@@ -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"

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 437

Quoted 9 lines
fs/xfs/scrub/refcount.c +6 -0
@@ -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

Quoted 6 lines
fs/xfs/scrub/refcount.c +6 -0
+	xfs_extlen_t			len)
+{
+	struct xfs_refcount_irec	rc;
+	bool				has_cowflag;
+	int				has_refcount;
+	int				error;

.....

fs/xfs/scrub/refcount.c xfs_scrub_refcountbt( · line 483

Quoted 8 lines
fs/xfs/scrub/refcount.c +8 -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,

agbno

Quoted 10 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#82 lore raw

PATCH v3 19/21 xfs: cross-reference refcount btree during scrub

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

---
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(-)
fs/xfs/scrub/agheader.c +25 -0
index 1d109d5..20a3beb 100644
@@ -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 */
 }
fs/xfs/scrub/alloc.c +1 -0
index 3faa437..517c079 100644
@@ -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. */
fs/xfs/scrub/bmap.c +15 -0
index 933e0b8..7b2cf8f 100644
@@ -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);
 }
fs/xfs/scrub/ialloc.c +1 -0
index 1a16f78..21c850a 100644
@@ -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? */
fs/xfs/scrub/inode.c +36 -14

76 lines not on this page · raw message

fs/xfs/scrub/refcount.c +67 -0

76 lines not on this page · raw message

fs/xfs/scrub/rmap.c +37 -0
index 3ee5061..8f2a7c3 100644
@@ -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. */
fs/xfs/scrub/scrub.h +4 -0
index 8fcf491..c8f8b42 100644
@@ -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__ */
Reviewed-by on v3 19/21
#83 lore raw

Re: [PATCH v3 19/21] xfs: cross-reference refcount btree during scrub

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

During metadata btree scrub, we should cross-reference with the reference counts.

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

Signature · 2 lines
Dave Chinner
[email protected]
#84 lore raw

PATCH v11 20/21 xfs: cross-reference the realtime bitmap

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

---
 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(+)
fs/xfs/libxfs/xfs_rtbitmap.c +30 -0
index 3fb29a5..a3f25a2 100644
@@ -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;
+}
fs/xfs/scrub/bmap.c +2 -0
index 79c7767..f880c9b 100644
@@ -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. */
fs/xfs/scrub/rtbitmap.c +20 -0
index 6860d5d..bdf1484 100644
@@ -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);
+}
fs/xfs/scrub/scrub.h +6 -0
index 83cdcd8..91cc094 100644
@@ -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__ */
fs/xfs/xfs_rtalloc.h +4 -0
index 3f30f84..540d506 100644
@@ -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 */
#85 lore raw

PATCH v2 20/21 xfs: cross-reference the realtime bitmap

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

---
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(+)
fs/xfs/libxfs/xfs_rtbitmap.c +30 -0
index 3fb29a5..a3f25a2 100644
@@ -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;
+}
fs/xfs/scrub/bmap.c +3 -0
index 5c34e025..2908a1b 100644
@@ -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. */
fs/xfs/scrub/rtbitmap.c +20 -0
index 6860d5d..1828b17 100644
@@ -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);
+}
fs/xfs/scrub/scrub.h +6 -0
index 06c2d17..0b21f34 100644
@@ -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__ */
fs/xfs/xfs_rtalloc.h +4 -0
index 3f30f84..540d506 100644
@@ -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 */
#86 lore raw

Re: [PATCH v2 20/21] xfs: cross-reference the realtime bitmap

Quoted 22 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

---
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(+)
fs/xfs/libxfs/xfs_rtbitmap.c +0 -0
index 3fb29a5..a3f25a2 100644
@@ -1097,3 +1097,33 @@ xfs_verify_rtbno(
 {

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1107

Quoted 10 lines
fs/xfs/libxfs/xfs_rtbitmap.c +8 -0
 	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?

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1111

Quoted 4 lines
fs/xfs/libxfs/xfs_rtbitmap.c +4 -0
+	bool				*is_free)
+{
+	xfs_rtblock_t			end;
+	xfs_extlen_t			clen;

clen? Not sure what that is shorthand for.

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1117

Quoted 6 lines
fs/xfs/libxfs/xfs_rtbitmap.c +6 -0
+	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;
Quoted 17 lines
fs/xfs/libxfs/xfs_rtbitmap.c +12 -0
+		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;
+}
fs/xfs/scrub/bmap.c +0 -0
index 5c34e025..2908a1b 100644

fs/xfs/scrub/bmap.c xfs_scrub_bmap_rt_extent_xref( · line 283

Quoted 6 lines
fs/xfs/scrub/bmap.c +3 -0
@@ -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....

Quoted 9 lines
fs/xfs/scrub/bmap.c +0 -0
 }
 
 /* Cross-reference a single datadev extent record. */
fs/xfs/scrub/rtbitmap.c +0 -0
index 6860d5d..1828b17 100644
@@ -98,3 +98,23 @@ xfs_scrub_rtsummary(
 	/* XXX: implement this some day */

fs/xfs/scrub/rtbitmap.c xfs_scrub_rtsummary( · line 108

Quoted 10 lines
fs/xfs/scrub/rtbitmap.c +8 -0
 	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.

Signature · 2 lines
Dave Chinner
[email protected]
#87 lore raw

Re: [PATCH v2 20/21] xfs: cross-reference the realtime bitmap

Quoted 36 lines · Dave Chinner
Quoted 22 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

---
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(+)
fs/xfs/libxfs/xfs_rtbitmap.c +0 -0
index 3fb29a5..a3f25a2 100644
@@ -1097,3 +1097,33 @@ xfs_verify_rtbno(
 {

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1107

Quoted 10 lines
fs/xfs/libxfs/xfs_rtbitmap.c +8 -0
 	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?

Quoted 6 lines

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1111

Quoted 4 lines
fs/xfs/libxfs/xfs_rtbitmap.c +4 -0
+	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.

Quoted 13 lines

fs/xfs/libxfs/xfs_rtbitmap.c xfs_verify_rtbno( · line 1117

Quoted 7 lines
fs/xfs/libxfs/xfs_rtbitmap.c +6 -0
+	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

Quoted 4 lines
	if (len > UINT_MAX)
		clen = UINT_MAX;
	else
		clen = len;

Yes.

Quoted 50 lines
Quoted 18 lines
fs/xfs/libxfs/xfs_rtbitmap.c +12 -0
+		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;
+}
fs/xfs/scrub/bmap.c +0 -0
index 5c34e025..2908a1b 100644

fs/xfs/scrub/bmap.c xfs_scrub_bmap_rt_extent_xref( · line 283

Quoted 6 lines
fs/xfs/scrub/bmap.c +3 -0
@@ -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....

Quoted 9 lines
fs/xfs/scrub/bmap.c +0 -0
 }
 
 /* Cross-reference a single datadev extent record. */
fs/xfs/scrub/rtbitmap.c +0 -0
index 6860d5d..1828b17 100644
@@ -98,3 +98,23 @@ xfs_scrub_rtsummary(
 	/* XXX: implement this some day */

fs/xfs/scrub/rtbitmap.c xfs_scrub_rtsummary( · line 108

Quoted 10 lines
fs/xfs/scrub/rtbitmap.c +8 -0
 	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...

Quoted 3 lines

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

Quoted 11 lines

Cheers,

Dave.

Signature · 6 lines
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#88 lore raw

PATCH v3 20/21 xfs: cross-reference the realtime bitmap

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

---
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(+)
fs/xfs/libxfs/xfs_rtbitmap.c +21 -0
index 3fb29a5..106be2d 100644
@@ -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;
+}
fs/xfs/scrub/bmap.c +3 -0
index 7b2cf8f..2bb3e0c 100644
@@ -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. */
fs/xfs/scrub/rtbitmap.c +23 -0
index 6860d5d..2639099 100644
@@ -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);
+}
fs/xfs/scrub/scrub.h +6 -0
index c8f8b42..0d92af8 100644
@@ -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__ */
fs/xfs/xfs_rtalloc.h +4 -0
index 3f30f84..dfee3c9 100644
@@ -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 */
Reviewed-by on v3 20/21
#89 lore raw

Re: [PATCH v3 20/21] xfs: cross-reference the realtime bitmap

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

While we're scrubbing various btrees, cross-reference the records with the other metadata.

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

Quoted 7 lines
---
 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.

Signature · 2 lines
Dave Chinner
[email protected]
#90 lore raw

PATCH v11 21/21 xfs: cross-reference the block mappings when possible

In reply to Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Use an inode's block mappings to cross-reference inode block counters.

---
 fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
fs/xfs/scrub/inode.c +34 -0
index b32a01d..d21866f 100644
@@ -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);
 }
#91 lore raw

PATCH v2 21/21 xfs: cross-reference the block mappings when possible

From: Darrick J. Wong <[email protected]>

Use an inode's block mappings to cross-reference inode block counters.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
fs/xfs/scrub/inode.c +34 -0
index f12586c..b2fc3ac 100644
@@ -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);
 }
Reviewed-by on v2 21/21
#92 lore raw

Re: [PATCH v2 21/21] xfs: cross-reference the block mappings when possible

Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Use an inode's block mappings to cross-reference inode block counters.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

Looks fine.

Signature · 2 lines
Dave Chinner
[email protected]
#93 lore raw

Re: [PATCH v2 21/21] xfs: cross-reference the block mappings when possible

Quoted 12 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong

From: Darrick J. Wong <[email protected]>

Use an inode's block mappings to cross-reference inode block counters.

---
v2: streamline scrubber arguments, remove stack allocated objects

---
 fs/xfs/scrub/inode.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
Quoted 3 lines

Looks fine.

Wooo, thanks for the review!

--D

Quoted 8 lines
Signature · 7 lines
-- 
Dave Chinner
[email protected]
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html