[PATCH 23/25] xfs: cross-reference refcount btree during scrub

"Darrick J. Wong" <[email protected]>
Newsgroups gmane.comp.file-systems.xfs.general
Message-ID <[email protected]>
During metadata btree scrub, we should cross-reference with the
reference counts.

Signed-off-by: Darrick J. Wong <[email protected]>
---
 fs/xfs/libxfs/xfs_refcount.c |   19 ++++
 fs/xfs/libxfs/xfs_refcount.h |    3 +
 fs/xfs/xfs_scrub.c           |  184 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+)


diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 9136745..af82ea3 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -1535,3 +1535,22 @@ xfs_refcount_free_cow_extent(
 	return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
 			fsb, len);
 }
+
+/* Is there a record covering a given extent? */
+int
+xfs_refcount_has_record(
+	struct xfs_btree_cur	*cur,
+	xfs_agblock_t		bno,
+	xfs_extlen_t		len,
+	bool			*exists)
+{
+	union xfs_btree_irec	low;
+	union xfs_btree_irec	high;
+
+	memset(&low, 0, sizeof(low));
+	low.rc.rc_startblock = bno;
+	memset(&high, 0xFF, sizeof(high));
+	high.rc.rc_startblock = bno + len - 1;
+
+	return xfs_btree_has_record(cur, &low, &high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index 105c246..a00400f 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -64,4 +64,7 @@ extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
 		struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
 		xfs_extlen_t len);
 
+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__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 34c23f7..ff55d8c 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -867,6 +867,7 @@ xfs_scrub_sb(
 	bool				is_freesp;
 	bool				has_inodes;
 	bool				has_rmap;
+	bool				has_refcount;
 	int				error;
 	int				err2;
 
@@ -973,6 +974,17 @@ btree_xref:
 						  XFS_BTREE_NOERROR);
 	}
 
+	/* Cross-reference with the refcountbt. */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+		err2 = xfs_refcount_has_record(xcur, XFS_SB_BLOCK(mp), 1,
+				&has_refcount);
+		if (!err2)
+			XFS_SCRUB_CHECK(mp, bp, "superblock", !has_refcount);
+		xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+						  XFS_BTREE_NOERROR);
+	}
+
 	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
 out:
 	xfs_buf_relse(bp);
@@ -1000,6 +1012,7 @@ xfs_scrub_agf(
 	bool				is_freesp;
 	bool				has_inodes;
 	bool				has_rmap;
+	bool				has_refcount;
 	int				error;
 	int				err2;
 
@@ -1103,6 +1116,23 @@ skip_rmap_xref:
 						  XFS_BTREE_NOERROR);
 	}
 
+	/* Cross-reference with the refcountbt. */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+		err2 = xfs_refcount_has_record(xcur, XFS_AGF_BLOCK(mp), 1,
+				&has_refcount);
+		if (err2)
+			goto skip_refc_xref;
+		XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !has_refcount);
+		err2 = xfs_btree_count_blocks(xcur, &blocks);
+		if (!err2)
+			XFS_SCRUB_CHECK(mp, agf_bp, "AGF", blocks ==
+					be32_to_cpu(agf->agf_refcount_blocks));
+skip_refc_xref:
+		xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+						  XFS_BTREE_NOERROR);
+	}
+
 	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
 	return error;
 }
@@ -1123,6 +1153,7 @@ xfs_scrub_agfl(
 	struct xfs_btree_cur		*icur = NULL;
 	struct xfs_btree_cur		*fcur = NULL;
 	struct xfs_btree_cur		*rcur = NULL;
+	struct xfs_btree_cur		*ccur = NULL;
 	struct xfs_owner_info		oinfo;
 	xfs_agnumber_t			agno;
 	xfs_agblock_t			agbno;
@@ -1131,6 +1162,7 @@ xfs_scrub_agfl(
 	bool				is_freesp;
 	bool				has_inodes;
 	bool				has_rmap;
+	bool				has_refcount;
 	int				i;
 	int				error;
 	int				err2;
@@ -1185,6 +1217,15 @@ xfs_scrub_agfl(
 			XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
 	}
 
+	/* Set up cross-reference with refcountbt. */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		ccur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+		err2 = xfs_refcount_has_record(ccur, XFS_AGFL_BLOCK(mp), 1,
+				&has_refcount);
+		if (!err2)
+			XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_refcount);
+	}
+
 	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG);
 	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
 	for (i = be32_to_cpu(agf->agf_flfirst);
@@ -1229,8 +1270,19 @@ xfs_scrub_agfl(
 			if (!err2)
 				XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
 		}
+
+		/* Cross-reference with the refcountbt. */
+		if (ccur) {
+			err2 = xfs_refcount_has_record(ccur, agbno, 1,
+					&has_refcount);
+			if (!err2)
+				XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+						!has_refcount);
+		}
 	}
 
+	if (ccur)
+		xfs_btree_del_cursor(ccur, XFS_BTREE_ERROR);
 	if (rcur)
 		xfs_btree_del_cursor(rcur, XFS_BTREE_ERROR);
 	if (fcur)
@@ -1264,6 +1316,7 @@ xfs_scrub_agi(
 	bool				is_freesp;
 	bool				has_inodes;
 	bool				has_rmap;
+	bool				has_refcount;
 	int				error;
 	int				err2;
 
@@ -1334,6 +1387,17 @@ xfs_scrub_agi(
 						  XFS_BTREE_NOERROR);
 	}
 
+	/* Cross-reference with the refcountbt. */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+		err2 = xfs_refcount_has_record(xcur, XFS_AGI_BLOCK(mp), 1,
+				&has_refcount);
+		if (!err2)
+			XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !has_refcount);
+		xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+						  XFS_BTREE_NOERROR);
+	}
+
 	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
 	return error;
 }
@@ -1355,6 +1419,7 @@ xfs_scrub_allocbt_helper(
 	xfs_extlen_t			len;
 	bool				has_rmap;
 	bool				has_inodes;
+	bool				has_refcount;
 	int				has_otherrec;
 	int				error = 0;
 	int				err2;
@@ -1415,6 +1480,14 @@ skip_freesp_xref:
 			XFS_BTREC_SCRUB_CHECK(bs, !has_rmap);
 	}
 
+	/* Cross-reference with the refcountbt. */
+	if (bs->refc_cur) {
+		err2 = xfs_refcount_has_record(bs->refc_cur, bno, len,
+				&has_refcount);
+		if (!err2)
+			XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+	}
+
 	return error;
 }
 
@@ -1488,6 +1561,7 @@ xfs_scrub_iallocbt_helper(
 	bool				is_freesp;
 	bool				has_inodes;
 	bool				has_rmap;
+	bool				has_refcount;
 	int				holecount;
 	int				i;
 	int				error = 0;
@@ -1547,6 +1621,14 @@ xfs_scrub_iallocbt_helper(
 				XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
 		}
 
+		/* Cross-reference with the refcountbt. */
+		if (bs->refc_cur) {
+			err2 = xfs_refcount_has_record(bs->refc_cur, bno,
+					len, &has_refcount);
+			if (!err2)
+				XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+		}
+
 		goto out;
 	}
 
@@ -1604,6 +1686,14 @@ xfs_scrub_iallocbt_helper(
 			if (!err2)
 				XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
 		}
+
+		/* Cross-reference with the refcountbt. */
+		if (bs->refc_cur) {
+			err2 = xfs_refcount_has_record(bs->refc_cur, bno,
+					len, &has_refcount);
+			if (!err2)
+				XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+		}
 	}
 
 	XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
@@ -1674,13 +1764,17 @@ xfs_scrub_rmapbt_helper(
 	struct xfs_mount		*mp = bs->cur->bc_mp;
 	struct xfs_agf			*agf;
 	struct xfs_rmap_irec		irec;
+	struct xfs_refcount_irec	crec;
 	xfs_agblock_t			eoag;
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
 	bool				is_freesp;
 	bool				non_inode;
 	bool				is_unwritten;
 	bool				is_bmbt;
 	bool				is_attr;
 	bool				has_inodes;
+	int				has_refcount;
 	int				error = 0;
 	int				err2;
 
@@ -1741,6 +1835,45 @@ xfs_scrub_rmapbt_helper(
 					!has_inodes);
 	}
 
+	/* Cross-reference with the refcount btree. */
+	if (bs->refc_cur) {
+		if (irec.rm_owner == XFS_RMAP_OWN_COW) {
+			/* Check this CoW staging extent. */
+			err2 = xfs_refcount_lookup_le(bs->refc_cur,
+					irec.rm_startblock, &has_refcount);
+			if (err2)
+				goto skip_refc_xref;
+			XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref);
+
+			err2 = xfs_refcount_get_rec(bs->refc_cur, &crec,
+					&has_refcount);
+			if (err2)
+				goto skip_refc_xref;
+			XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref);
+			XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock <=
+					irec.rm_startblock);
+			XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock +
+					crec.rc_blockcount >
+					crec.rc_startblock);
+			XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock +
+					crec.rc_blockcount >=
+					irec.rm_startblock +
+					irec.rm_blockcount);
+			XFS_BTREC_SCRUB_CHECK(bs,
+					crec.rc_refcount == 1);
+		} else {
+			/* If this is shared, the inode flag must be set. */
+			err2 = xfs_refcount_find_shared(bs->refc_cur,
+					irec.rm_startblock, irec.rm_blockcount,
+					&fbno, &flen, false);
+			if (!err2)
+				XFS_BTREC_SCRUB_CHECK(bs, flen == 0 ||
+						(!non_inode && !is_attr &&
+						 !is_bmbt && !is_unwritten));
+		}
+skip_refc_xref:;
+	}
+
 	return error;
 }
 
@@ -2164,12 +2297,16 @@ xfs_scrub_bmap_extent(
 	xfs_agnumber_t			agno;
 	xfs_fsblock_t			bno;
 	struct xfs_rmap_irec		rmap;
+	struct xfs_refcount_irec	crec;
 	uint64_t			owner;
 	xfs_fileoff_t			offset;
+	xfs_agblock_t			fbno;
+	xfs_extlen_t			flen;
 	bool				is_freesp;
 	bool				has_inodes;
 	unsigned int			rflags;
 	int				has_rmap;
+	int				has_refcount;
 	int				error = 0;
 	int				err2 = 0;
 
@@ -2348,6 +2485,53 @@ skip_rmap_xref:
 						  XFS_BTREE_NOERROR);
 	}
 
+	/*
+	 * If this is a non-shared file on a reflink filesystem,
+	 * check the refcountbt to see if the flag is wrong.
+	 */
+	if (xfs_sb_version_hasreflink(&mp->m_sb) && !info->is_rt) {
+		xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+
+		if (info->whichfork == XFS_COW_FORK) {
+			/* Check this CoW staging extent. */
+			err2 = xfs_refcount_lookup_le(xcur, bno, &has_refcount);
+			if (err2)
+				goto skip_refc_xref;
+			XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount,
+					skip_refc_xref);
+
+			err2 = xfs_refcount_get_rec(xcur, &crec, &has_refcount);
+			if (err2)
+				goto skip_refc_xref;
+			XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount,
+					skip_refc_xref);
+
+			XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+					crec.rc_startblock <= bno);
+			XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+					crec.rc_startblock +
+					crec.rc_blockcount >
+					crec.rc_startblock);
+			XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+					crec.rc_startblock +
+					crec.rc_blockcount >=
+					bno + irec->br_blockcount);
+			XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+					crec.rc_refcount == 1);
+		} else {
+			/* If this is shared, the inode flag must be set. */
+			err2 = xfs_refcount_find_shared(xcur, bno,
+					irec->br_blockcount, &fbno, &flen,
+					false);
+			if (!err2)
+				XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+						flen == 0 ||
+						xfs_is_reflink_inode(ip));
+		}
+skip_refc_xref:
+		xfs_btree_del_cursor(xcur, XFS_BTREE_NOERROR);
+	}
+
 	xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
 out:
 	info->lastoff = irec->br_startoff + irec->br_blockcount;

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.