Skip to contentSkip to conversation

xfs: online scrub support

PATCH Superseded Darrick J. Wong 3 participants 51 messages Sep 21, 2017 – Sep 23, 2017 XFS filesystem

v10 of 22 Superseded Latest is v22 →

lore
Share & tools
lore
#0 lore raw

v10 cover xfs: online scrub support

Hi all,

This is the ninth revision of a patchset that adds to XFS kernel support for online metadata scrubbing and repair. There aren't any on-disk format changes. Changes since v9 include minor bug fixes and rebasing to 4.14. I have been performing daily online scrubs of my XFS filesystems for several months now, with surprisingly few problems.

Online scrub/repair support consists of four major pieces -- first, an ioctl that maps physical extents to their owners (GETFSMAP; already in 4.12); second, various in-kernel metadata scrubbing ioctls to examine metadata records and cross-reference them with other filesystem metadata; third, an in-kernel mechanism for rebuilding damaged metadata objects and btrees; and fourth, a userspace component to coordinate scrubbing and repair operations.

This new utility, xfs_scrub, is separate from the existing offline xfs_repair tool. The program uses various XFS ioctls to iterate all XFS metadata and asks the kernel to check the metadata and repair it if necessary.

While I understand that reviewer bandwidth is limited, I would like to get this series prepped for 4.15, if possible. I have isolated the scrub code such that it can be compiled out entirely, in the hopes that we can stabilize the code while not exposing regular users to riskier code.

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.14-rc1. 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 v10 1/27 xfs: return a distinct error code value for IGET_INCORE cache misses

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

For an XFS_IGET_INCORE iget operation, if the inode isn't in the cache, return ENODATA so that we don't confuse it with the pre-existing ENOENT cases (inode is in cache, but freed).

---
 fs/xfs/xfs_icache.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_icache.c +1 -1
index 3422711..43005fb 100644
@@ -610,7 +610,7 @@ xfs_iget(
 	} else {
 		rcu_read_unlock();
 		if (flags & XFS_IGET_INCORE) {
-			error = -ENOENT;
+			error = -ENODATA;
 			goto out_error_or_again;
 		}
 		XFS_STATS_INC(mp, xs_ig_missed);
Reviewed-by on v10 1/27
#2 lore raw

Re: [PATCH 01/27] xfs: return a distinct error code value for IGET_INCORE cache misses

Quoted 8 lines · Darrick J. Wong

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

For an XFS_IGET_INCORE iget operation, if the inode isn't in the cache, return ENODATA so that we don't confuse it with the pre-existing ENOENT cases (inode is in cache, but freed).

---

Quoted 22 lines
 fs/xfs/xfs_icache.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_icache.c +1 -2
index 3422711..43005fb 100644
@@ -610,7 +610,7 @@ xfs_iget(
 	} else {
 		rcu_read_unlock();
 		if (flags & XFS_IGET_INCORE) {
-			error = -ENOENT;
+			error = -ENODATA;
 			goto out_error_or_again;
 		}
 		XFS_STATS_INC(mp, xs_ig_missed);
 
--

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

#3 lore raw

PATCH v10 2/27 xfs: query the per-AG reservation counters

In reply to Darrick J. Wong

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

Establish an ioctl for userspace to query the original and current per-AG reservation counts. This will be used by xfs_scrub to check that the vfs counters are at least somewhat sane.

---
 fs/xfs/libxfs/xfs_fs.h |   12 ++++++++++++
 fs/xfs/xfs_fsops.c     |   26 ++++++++++++++++++++++++++
 fs/xfs/xfs_fsops.h     |    2 ++
 fs/xfs/xfs_ioctl.c     |   24 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c   |    1 +
 5 files changed, 65 insertions(+)
fs/xfs/libxfs/xfs_fs.h +12 -0
index 8c61f21..2c26c38 100644
@@ -469,6 +469,17 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
 /*
+ * AG reserved block counters
+ */
+struct xfs_fsop_ag_resblks {
+	__u32 ar_flags;			/* output flags, none defined now */
+	__u32 ar_reserved;		/* zero */
+	__u64 ar_current_resv;		/* blocks reserved now */
+	__u64 ar_mount_resv;		/* blocks reserved at mount time */
+	__u64 ar_reserved2[5];		/* zero */
+};
+
+/*
  * ioctl limits
  */
 #ifdef XATTR_LIST_MAX
@@ -543,6 +554,7 @@ typedef struct xfs_swapext
 #define XFS_IOC_ATTRMULTI_BY_HANDLE  _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
 #define XFS_IOC_FSGEOMETRY	     _IOR ('X', 124, struct xfs_fsop_geom)
 #define XFS_IOC_GOINGDOWN	     _IOR ('X', 125, uint32_t)
+#define XFS_IOC_GET_AG_RESBLKS	     _IOR ('X', 126, struct xfs_fsop_ag_resblks)
 /*	XFS_IOC_GETFSUUID ---------- deprecated 140	 */
 
 
fs/xfs/xfs_fsops.c +26 -0
index 8f22fc5..50fb3a2 100644
@@ -44,6 +44,7 @@
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
+#include "xfs_fs.h"
 
 /*
  * File system operations
@@ -1046,3 +1047,28 @@ xfs_fs_unreserve_ag_blocks(
 
 	return error;
 }
+
+/* Query the per-AG reservations to see how many blocks we have reserved. */
+int
+xfs_fs_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	struct xfs_fsop_ag_resblks	*out)
+{
+	struct xfs_ag_resv		*r;
+	struct xfs_perag		*pag;
+	xfs_agnumber_t			agno;
+
+	memset(out, 0, sizeof(struct xfs_fsop_ag_resblks));
+	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+		pag = xfs_perag_get(mp, agno);
+		r = xfs_perag_resv(pag, XFS_AG_RESV_METADATA);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		r = xfs_perag_resv(pag, XFS_AG_RESV_AGFL);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		xfs_perag_put(pag);
+	}
+
+	return 0;
+}
fs/xfs/xfs_fsops.h +2 -0
index 2954c13..c8f5e26 100644
@@ -25,6 +25,8 @@ extern int xfs_fs_counts(xfs_mount_t *mp, xfs_fsop_counts_t *cnt);
 extern int xfs_reserve_blocks(xfs_mount_t *mp, uint64_t *inval,
 				xfs_fsop_resblks_t *outval);
 extern int xfs_fs_goingdown(xfs_mount_t *mp, uint32_t inflags);
+extern int xfs_fs_get_ag_reserve_blocks(struct xfs_mount *mp,
+		struct xfs_fsop_ag_resblks *out);
 
 extern int xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
 extern int xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
fs/xfs/xfs_ioctl.c +24 -0
index 5049e8a..44dc178 100644
@@ -1782,6 +1782,27 @@ xfs_ioc_swapext(
 	return error;
 }
 
+static int
+xfs_ioc_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	void __user			*arg)
+{
+	struct xfs_fsop_ag_resblks	out;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	error = xfs_fs_get_ag_reserve_blocks(mp, &out);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &out, sizeof(out)))
+		return -EFAULT;
+
+	return 0;
+}
+
 /*
  * Note: some of the ioctl's return positive numbers as a
  * byte count indicating success, such as readlink_by_handle.
@@ -1987,6 +2008,9 @@ xfs_file_ioctl(
 		return 0;
 	}
 
+	case XFS_IOC_GET_AG_RESBLKS:
+		return xfs_ioc_get_ag_reserve_blocks(mp, arg);
+
 	case XFS_IOC_FSGROWFSDATA: {
 		xfs_growfs_data_t in;
 
fs/xfs/xfs_ioctl32.c +1 -0
index fa0bc4d..e8b4de3 100644
@@ -556,6 +556,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_INJECTION:
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
+	case XFS_IOC_GET_AG_RESBLKS:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
#4 lore raw

Re: [PATCH 02/27] xfs: query the per-AG reservation counters

Quoted 8 lines · Darrick J. Wong

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

Establish an ioctl for userspace to query the original and current per-AG reservation counts. This will be used by xfs_scrub to check that the vfs counters are at least somewhat sane.

---

The code seems fine, but I'm wondering how much of this would remain as is based on Dave's potential switch of the perag res stuff to be a hidden reservation. That itself still needs to be posted/reviewed/etc., so might it be smarter to defer defining an interface to export this stuff until the core mechanism is worked out?

That aside, I'm also wondering how/what userspace scrub does with the overall total values as opposed to individual AG reservation values. Are the current "ask" values expected to be uniform across each AG..? For example, what happens if one AG has a bogus "current" value for whatever reason (perhaps this is more relevant for both values if we do actually end up with on-disk reservations)?

Brian

Quoted 151 lines
 fs/xfs/libxfs/xfs_fs.h |   12 ++++++++++++
 fs/xfs/xfs_fsops.c     |   26 ++++++++++++++++++++++++++
 fs/xfs/xfs_fsops.h     |    2 ++
 fs/xfs/xfs_ioctl.c     |   24 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c   |    1 +
 5 files changed, 65 insertions(+)
fs/xfs/libxfs/xfs_fs.h +12 -0
index 8c61f21..2c26c38 100644
@@ -469,6 +469,17 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
 /*
+ * AG reserved block counters
+ */
+struct xfs_fsop_ag_resblks {
+	__u32 ar_flags;			/* output flags, none defined now */
+	__u32 ar_reserved;		/* zero */
+	__u64 ar_current_resv;		/* blocks reserved now */
+	__u64 ar_mount_resv;		/* blocks reserved at mount time */
+	__u64 ar_reserved2[5];		/* zero */
+};
+
+/*
  * ioctl limits
  */
 #ifdef XATTR_LIST_MAX
@@ -543,6 +554,7 @@ typedef struct xfs_swapext
 #define XFS_IOC_ATTRMULTI_BY_HANDLE  _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
 #define XFS_IOC_FSGEOMETRY	     _IOR ('X', 124, struct xfs_fsop_geom)
 #define XFS_IOC_GOINGDOWN	     _IOR ('X', 125, uint32_t)
+#define XFS_IOC_GET_AG_RESBLKS	     _IOR ('X', 126, struct xfs_fsop_ag_resblks)
 /*	XFS_IOC_GETFSUUID ---------- deprecated 140	 */
 
 
fs/xfs/xfs_fsops.c +26 -0
index 8f22fc5..50fb3a2 100644
@@ -44,6 +44,7 @@
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
+#include "xfs_fs.h"
 
 /*
  * File system operations
@@ -1046,3 +1047,28 @@ xfs_fs_unreserve_ag_blocks(
 
 	return error;
 }
+
+/* Query the per-AG reservations to see how many blocks we have reserved. */
+int
+xfs_fs_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	struct xfs_fsop_ag_resblks	*out)
+{
+	struct xfs_ag_resv		*r;
+	struct xfs_perag		*pag;
+	xfs_agnumber_t			agno;
+
+	memset(out, 0, sizeof(struct xfs_fsop_ag_resblks));
+	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+		pag = xfs_perag_get(mp, agno);
+		r = xfs_perag_resv(pag, XFS_AG_RESV_METADATA);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		r = xfs_perag_resv(pag, XFS_AG_RESV_AGFL);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		xfs_perag_put(pag);
+	}
+
+	return 0;
+}
fs/xfs/xfs_fsops.h +2 -0
index 2954c13..c8f5e26 100644
@@ -25,6 +25,8 @@ extern int xfs_fs_counts(xfs_mount_t *mp, xfs_fsop_counts_t *cnt);
 extern int xfs_reserve_blocks(xfs_mount_t *mp, uint64_t *inval,
 				xfs_fsop_resblks_t *outval);
 extern int xfs_fs_goingdown(xfs_mount_t *mp, uint32_t inflags);
+extern int xfs_fs_get_ag_reserve_blocks(struct xfs_mount *mp,
+		struct xfs_fsop_ag_resblks *out);
 
 extern int xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
 extern int xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
fs/xfs/xfs_ioctl.c +24 -0
index 5049e8a..44dc178 100644
@@ -1782,6 +1782,27 @@ xfs_ioc_swapext(
 	return error;
 }
 
+static int
+xfs_ioc_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	void __user			*arg)
+{
+	struct xfs_fsop_ag_resblks	out;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	error = xfs_fs_get_ag_reserve_blocks(mp, &out);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &out, sizeof(out)))
+		return -EFAULT;
+
+	return 0;
+}
+
 /*
  * Note: some of the ioctl's return positive numbers as a
  * byte count indicating success, such as readlink_by_handle.
@@ -1987,6 +2008,9 @@ xfs_file_ioctl(
 		return 0;
 	}
 
+	case XFS_IOC_GET_AG_RESBLKS:
+		return xfs_ioc_get_ag_reserve_blocks(mp, arg);
+
 	case XFS_IOC_FSGROWFSDATA: {
 		xfs_growfs_data_t in;
 
fs/xfs/xfs_ioctl32.c +1 -1
index fa0bc4d..e8b4de3 100644
@@ -556,6 +556,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_INJECTION:
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
+	case XFS_IOC_GET_AG_RESBLKS:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
 
--

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

#5 lore raw

Re: [PATCH 02/27] xfs: query the per-AG reservation counters

Quoted 7 lines · Brian Foster
Quoted 6 lines · Darrick J. Wong

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

Establish an ioctl for userspace to query the original and current per-AG reservation counts. This will be used by xfs_scrub to check that the vfs counters are at least somewhat sane.

Quoted 8 lines
Quoted 2 lines

---

The code seems fine, but I'm wondering how much of this would remain as is based on Dave's potential switch of the perag res stuff to be a hidden reservation. That itself still needs to be posted/reviewed/etc., so might it be smarter to defer defining an interface to export this stuff until the core mechanism is worked out?

I think Dave's vision is that we should permanently steal the AG reservations from the max fs size:

(device size - static metadata - static per-ag reservations)

In which case the ioctl isn't needed at all, since the statfs counters would simply never report the per-ag reservations at all.

Quoted 2 lines

That aside, I'm also wondering how/what userspace scrub does with the overall total values as opposed to individual AG reservation values.

To a rough approximation, xfs_scrub (phase 7) compares the fs geometry data (used blocks, used inodes) against what it found via getfsmap and bulkstat and warns about a potentially insufficient scrub if the counts are off by more than 10% from what the fs reports. The per-AG summary counter ar_current_resv is added to the statvfs.f_bfree counter since reserved blocks don't show up in getfsmap.

Since per-AG reservations never use more than 9% of the fs space worst case (1k blocks) and usually 2% (4k blocks) the current 10% tolerance built into xfs_scrub is probably good enough for now.

Anyway, I plan to withdraw this patch.

Quoted 1 line

Are the current "ask" values expected to be uniform across each AG..?

scrub doesn't care.

Quoted 3 lines

For example, what happens if one AG has a bogus "current" value for whatever reason (perhaps this is more relevant for both values if we do actually end up with on-disk reservations)?

If it exceeds scrub's tolerances it'll warn about the discrepancy. There's probably no way to fix the summary counters without an xfs_repair run, unless it's acceptable to freeze the whole fs.

Not going to try to do that. :)

--D

Quoted 158 lines

Brian

Quoted 148 lines
 fs/xfs/libxfs/xfs_fs.h |   12 ++++++++++++
 fs/xfs/xfs_fsops.c     |   26 ++++++++++++++++++++++++++
 fs/xfs/xfs_fsops.h     |    2 ++
 fs/xfs/xfs_ioctl.c     |   24 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c   |    1 +
 5 files changed, 65 insertions(+)
fs/xfs/libxfs/xfs_fs.h +12 -0
index 8c61f21..2c26c38 100644
@@ -469,6 +469,17 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
 /*
+ * AG reserved block counters
+ */
+struct xfs_fsop_ag_resblks {
+	__u32 ar_flags;			/* output flags, none defined now */
+	__u32 ar_reserved;		/* zero */
+	__u64 ar_current_resv;		/* blocks reserved now */
+	__u64 ar_mount_resv;		/* blocks reserved at mount time */
+	__u64 ar_reserved2[5];		/* zero */
+};
+
+/*
  * ioctl limits
  */
 #ifdef XATTR_LIST_MAX
@@ -543,6 +554,7 @@ typedef struct xfs_swapext
 #define XFS_IOC_ATTRMULTI_BY_HANDLE  _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
 #define XFS_IOC_FSGEOMETRY	     _IOR ('X', 124, struct xfs_fsop_geom)
 #define XFS_IOC_GOINGDOWN	     _IOR ('X', 125, uint32_t)
+#define XFS_IOC_GET_AG_RESBLKS	     _IOR ('X', 126, struct xfs_fsop_ag_resblks)
 /*	XFS_IOC_GETFSUUID ---------- deprecated 140	 */
 
 
fs/xfs/xfs_fsops.c +26 -0
index 8f22fc5..50fb3a2 100644
@@ -44,6 +44,7 @@
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
+#include "xfs_fs.h"
 
 /*
  * File system operations
@@ -1046,3 +1047,28 @@ xfs_fs_unreserve_ag_blocks(
 
 	return error;
 }
+
+/* Query the per-AG reservations to see how many blocks we have reserved. */
+int
+xfs_fs_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	struct xfs_fsop_ag_resblks	*out)
+{
+	struct xfs_ag_resv		*r;
+	struct xfs_perag		*pag;
+	xfs_agnumber_t			agno;
+
+	memset(out, 0, sizeof(struct xfs_fsop_ag_resblks));
+	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+		pag = xfs_perag_get(mp, agno);
+		r = xfs_perag_resv(pag, XFS_AG_RESV_METADATA);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		r = xfs_perag_resv(pag, XFS_AG_RESV_AGFL);
+		out->ar_current_resv += r->ar_reserved;
+		out->ar_mount_resv += r->ar_asked;
+		xfs_perag_put(pag);
+	}
+
+	return 0;
+}
fs/xfs/xfs_fsops.h +2 -0
index 2954c13..c8f5e26 100644
@@ -25,6 +25,8 @@ extern int xfs_fs_counts(xfs_mount_t *mp, xfs_fsop_counts_t *cnt);
 extern int xfs_reserve_blocks(xfs_mount_t *mp, uint64_t *inval,
 				xfs_fsop_resblks_t *outval);
 extern int xfs_fs_goingdown(xfs_mount_t *mp, uint32_t inflags);
+extern int xfs_fs_get_ag_reserve_blocks(struct xfs_mount *mp,
+		struct xfs_fsop_ag_resblks *out);
 
 extern int xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
 extern int xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
fs/xfs/xfs_ioctl.c +24 -0
index 5049e8a..44dc178 100644
@@ -1782,6 +1782,27 @@ xfs_ioc_swapext(
 	return error;
 }
 
+static int
+xfs_ioc_get_ag_reserve_blocks(
+	struct xfs_mount		*mp,
+	void __user			*arg)
+{
+	struct xfs_fsop_ag_resblks	out;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	error = xfs_fs_get_ag_reserve_blocks(mp, &out);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &out, sizeof(out)))
+		return -EFAULT;
+
+	return 0;
+}
+
 /*
  * Note: some of the ioctl's return positive numbers as a
  * byte count indicating success, such as readlink_by_handle.
@@ -1987,6 +2008,9 @@ xfs_file_ioctl(
 		return 0;
 	}
 
+	case XFS_IOC_GET_AG_RESBLKS:
+		return xfs_ioc_get_ag_reserve_blocks(mp, arg);
+
 	case XFS_IOC_FSGROWFSDATA: {
 		xfs_growfs_data_t in;
 
fs/xfs/xfs_ioctl32.c +1 -1
index fa0bc4d..e8b4de3 100644
@@ -556,6 +556,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_INJECTION:
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
+	case XFS_IOC_GET_AG_RESBLKS:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
 
--
Quoted 3 lines

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

#6 lore raw

PATCH v10 3/27 xfs: create an ioctl to scrub AG metadata

In reply to Darrick J. Wong

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

Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.

---
 fs/xfs/Kconfig           |   17 ++++++++++++++
 fs/xfs/Makefile          |   11 +++++++++
 fs/xfs/libxfs/xfs_fs.h   |   53 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h     |   25 +++++++++++++++++++++
 fs/xfs/scrub/trace.c     |   41 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h     |   33 ++++++++++++++++++++++++++++
 fs/xfs/scrub/xfs_scrub.h |   29 +++++++++++++++++++++++++
 fs/xfs/xfs_ioctl.c       |   28 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c     |    1 +
 10 files changed, 292 insertions(+)
 create mode 100644 fs/xfs/scrub/scrub.c
 create mode 100644 fs/xfs/scrub/scrub.h
 create mode 100644 fs/xfs/scrub/trace.c
 create mode 100644 fs/xfs/scrub/trace.h
 create mode 100644 fs/xfs/scrub/xfs_scrub.h
fs/xfs/Kconfig +17 -0
index 1b98cfa..f42fcf1 100644
@@ -71,6 +71,23 @@ config XFS_RT
 
 	  If unsure, say N.
 
+config XFS_ONLINE_SCRUB
+	bool "XFS online metadata check support"
+	default n
+	depends on XFS_FS
+	help
+	  If you say Y here you will be able to check metadata on a
+	  mounted XFS filesystem.  This feature is intended to reduce
+	  filesystem downtime by supplementing xfs_repair.  The key
+	  advantage here is to look for problems proactively so that
+	  they can be dealt with in a controlled manner.
+
+	  This feature is considered EXPERIMENTAL.  Use with caution!
+
+	  See the xfs_scrub man page in section 8 for additional information.
+
+	  If unsure, say N.
+
 config XFS_WARN
 	bool "XFS Verbose Warnings"
 	depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
index dbc33e0..f4312bc 100644
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+
+# online scrub/repair
+ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
+
+# Tracepoints like to blow up, so build that before everything else
+
+xfs-y				+= $(addprefix scrub/, \
+				   trace.o \
+				   scrub.o \
+				   )
+endif
fs/xfs/libxfs/xfs_fs.h +53 -0

65 lines not on this page · raw message

fs/xfs/scrub/scrub.c new file +54 -0

54 lines not on this page · raw message

fs/xfs/scrub/scrub.h new file +25 -0
new file mode 100644
index 0000000..eb1cd9d
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_SCRUB_H__
+#define __XFS_SCRUB_SCRUB_H__
+
+/* Metadata scrubbers */
+
+#endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0

41 lines not on this page · raw message

fs/xfs/scrub/trace.h new file +33 -0
new file mode 100644
index 0000000..a95a7c8
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xfs_scrub
+
+#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_XFS_SCRUB_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#endif /* _TRACE_XFS_SCRUB_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE scrub/trace
+#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
new file mode 100644
index 0000000..e00e0ea
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_H__
+#define __XFS_SCRUB_H__
+
+#ifndef CONFIG_XFS_ONLINE_SCRUB
+# define xfs_scrub_metadata(ip, sm)	(-ENOTTY)
+#else
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
+#endif /* CONFIG_XFS_ONLINE_SCRUB */
+
+#endif	/* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
index 44dc178..ab7a7f8 100644
@@ -44,6 +44,7 @@
 #include "xfs_btree.h"
 #include <linux/fsmap.h>
 #include "xfs_fsmap.h"
+#include "scrub/xfs_scrub.h"
 
 #include <linux/capability.h>
 #include <linux/cred.h>
@@ -1702,6 +1703,30 @@ xfs_ioc_getfsmap(
 	return 0;
 }
 
+STATIC int
+xfs_ioc_scrub_metadata(
+	struct xfs_inode		*ip,
+	void				__user *arg)
+{
+	struct xfs_scrub_metadata	scrub;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&scrub, arg, sizeof(scrub)))
+		return -EFAULT;
+
+	error = xfs_scrub_metadata(ip, &scrub);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &scrub, sizeof(scrub)))
+		return -EFAULT;
+
+	return 0;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1906,6 +1931,9 @@ xfs_file_ioctl(
 	case FS_IOC_GETFSMAP:
 		return xfs_ioc_getfsmap(ip, arg);
 
+	case XFS_IOC_SCRUB_METADATA:
+		return xfs_ioc_scrub_metadata(ip, arg);
+
 	case XFS_IOC_FD_TO_HANDLE:
 	case XFS_IOC_PATH_TO_HANDLE:
 	case XFS_IOC_PATH_TO_FSHANDLE: {
fs/xfs/xfs_ioctl32.c +1 -0
index e8b4de3..972d4bd 100644
@@ -557,6 +557,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
 	case XFS_IOC_GET_AG_RESBLKS:
+	case XFS_IOC_SCRUB_METADATA:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
#7 lore raw

Re: [PATCH 03/27] xfs: create an ioctl to scrub AG metadata

Quoted 17 lines · Darrick J. Wong

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

Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.

---
 fs/xfs/Kconfig           |   17 ++++++++++++++
 fs/xfs/Makefile          |   11 +++++++++
 fs/xfs/libxfs/xfs_fs.h   |   53 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h     |   25 +++++++++++++++++++++
 fs/xfs/scrub/trace.c     |   41 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h     |   33 ++++++++++++++++++++++++++++
 fs/xfs/scrub/xfs_scrub.h |   29 +++++++++++++++++++++++++
Quoted 10 lines
 fs/xfs/xfs_ioctl.c       |   28 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c     |    1 +
 10 files changed, 292 insertions(+)
 create mode 100644 fs/xfs/scrub/scrub.c
 create mode 100644 fs/xfs/scrub/scrub.h
 create mode 100644 fs/xfs/scrub/trace.c
 create mode 100644 fs/xfs/scrub/trace.h
 create mode 100644 fs/xfs/scrub/xfs_scrub.h

The code looks sane, though I think I need to understand the error codes a bit better. Perhaps once I get further into the series..

Brian

Quoted 400 lines
fs/xfs/Kconfig +17 -0
index 1b98cfa..f42fcf1 100644
@@ -71,6 +71,23 @@ config XFS_RT
 
 	  If unsure, say N.
 
+config XFS_ONLINE_SCRUB
+	bool "XFS online metadata check support"
+	default n
+	depends on XFS_FS
+	help
+	  If you say Y here you will be able to check metadata on a
+	  mounted XFS filesystem.  This feature is intended to reduce
+	  filesystem downtime by supplementing xfs_repair.  The key
+	  advantage here is to look for problems proactively so that
+	  they can be dealt with in a controlled manner.
+
+	  This feature is considered EXPERIMENTAL.  Use with caution!
+
+	  See the xfs_scrub man page in section 8 for additional information.
+
+	  If unsure, say N.
+
 config XFS_WARN
 	bool "XFS Verbose Warnings"
 	depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
index dbc33e0..f4312bc 100644
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+
+# online scrub/repair
+ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
+
+# Tracepoints like to blow up, so build that before everything else
+
+xfs-y				+= $(addprefix scrub/, \
+				   trace.o \
+				   scrub.o \
+				   )
+endif
fs/xfs/libxfs/xfs_fs.h +53 -0
index 2c26c38..a4b4c8c 100644
@@ -468,6 +468,58 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
+/* metadata scrubbing */
+struct xfs_scrub_metadata {
+	__u32 sm_type;		/* What to check? */
+	__u32 sm_flags;		/* flags; see below. */
+	__u64 sm_ino;		/* inode number. */
+	__u32 sm_gen;		/* inode generation. */
+	__u32 sm_agno;		/* ag number. */
+	__u64 sm_reserved[5];	/* pad to 64 bytes */
+};
+
+/*
+ * Metadata types and flags for scrub operation.
+ */
+
+/* Scrub subcommands. */
+
+/* Number of scrub subcommands. */
+#define XFS_SCRUB_TYPE_NR	0
+
+/* i: Repair this metadata. */
+#define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
+
+/* o: Metadata object needs repair. */
+#define XFS_SCRUB_OFLAG_CORRUPT		(1 << 1)
+
+/*
+ * o: Metadata object could be optimized.  It's not corrupt, but
+ *    we could improve on it somehow.
+ */
+#define XFS_SCRUB_OFLAG_PREEN		(1 << 2)
+
+/* o: Cross-referencing failed. */
+#define XFS_SCRUB_OFLAG_XFAIL		(1 << 3)
+
+/* o: Metadata object disagrees with cross-referenced metadata. */
+#define XFS_SCRUB_OFLAG_XCORRUPT	(1 << 4)
+
+/* o: Scan was not complete. */
+#define XFS_SCRUB_OFLAG_INCOMPLETE	(1 << 5)
+
+/* o: Metadata object looked funny but isn't corrupt. */
+#define XFS_SCRUB_OFLAG_WARNING		(1 << 6)
+
+#define XFS_SCRUB_FLAGS_IN	(XFS_SCRUB_IFLAG_REPAIR)
+#define XFS_SCRUB_FLAGS_OUT	(XFS_SCRUB_OFLAG_CORRUPT | \
+				 XFS_SCRUB_OFLAG_PREEN | \
+				 XFS_SCRUB_OFLAG_XFAIL | \
+				 XFS_SCRUB_OFLAG_XCORRUPT | \
+				 XFS_SCRUB_OFLAG_INCOMPLETE | \
+				 XFS_SCRUB_OFLAG_WARNING)
+#define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+
 /*
  * AG reserved block counters
  */
@@ -522,6 +574,7 @@ struct xfs_fsop_ag_resblks {
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
 /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
+#define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/scrub/scrub.c new file +54 -0
new file mode 100644
index 0000000..5db2a6f
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/trace.h"
+
+/* Dispatch metadata scrubbing. */
+int
+xfs_scrub_metadata(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	return -EOPNOTSUPP;
+}
fs/xfs/scrub/scrub.h new file +25 -0
new file mode 100644
index 0000000..eb1cd9d
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_SCRUB_H__
+#define __XFS_SCRUB_SCRUB_H__
+
+/* Metadata scrubbers */
+
+#endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0
new file mode 100644
index 0000000..c59fd41
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_trans.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+
+/*
+ * We include this last to have the helpers above available for the trace
+ * event implementations.
+ */
+#define CREATE_TRACE_POINTS
+#include "scrub/trace.h"
fs/xfs/scrub/trace.h new file +33 -0
new file mode 100644
index 0000000..a95a7c8
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xfs_scrub
+
+#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_XFS_SCRUB_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#endif /* _TRACE_XFS_SCRUB_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE scrub/trace
+#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
new file mode 100644
index 0000000..e00e0ea
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_H__
+#define __XFS_SCRUB_H__
+
+#ifndef CONFIG_XFS_ONLINE_SCRUB
+# define xfs_scrub_metadata(ip, sm)	(-ENOTTY)
+#else
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
+#endif /* CONFIG_XFS_ONLINE_SCRUB */
+
+#endif	/* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
index 44dc178..ab7a7f8 100644
@@ -44,6 +44,7 @@
 #include "xfs_btree.h"
 #include <linux/fsmap.h>
 #include "xfs_fsmap.h"
+#include "scrub/xfs_scrub.h"
 
 #include <linux/capability.h>
 #include <linux/cred.h>
@@ -1702,6 +1703,30 @@ xfs_ioc_getfsmap(
 	return 0;
 }
 
+STATIC int
+xfs_ioc_scrub_metadata(
+	struct xfs_inode		*ip,
+	void				__user *arg)
+{
+	struct xfs_scrub_metadata	scrub;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&scrub, arg, sizeof(scrub)))
+		return -EFAULT;
+
+	error = xfs_scrub_metadata(ip, &scrub);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &scrub, sizeof(scrub)))
+		return -EFAULT;
+
+	return 0;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1906,6 +1931,9 @@ xfs_file_ioctl(
 	case FS_IOC_GETFSMAP:
 		return xfs_ioc_getfsmap(ip, arg);
 
+	case XFS_IOC_SCRUB_METADATA:
+		return xfs_ioc_scrub_metadata(ip, arg);
+
 	case XFS_IOC_FD_TO_HANDLE:
 	case XFS_IOC_PATH_TO_HANDLE:
 	case XFS_IOC_PATH_TO_FSHANDLE: {
fs/xfs/xfs_ioctl32.c +1 -1
index e8b4de3..972d4bd 100644
@@ -557,6 +557,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
 	case XFS_IOC_GET_AG_RESBLKS:
+	case XFS_IOC_SCRUB_METADATA:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
 
--

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

#8 lore raw

Re: [PATCH 03/27] xfs: create an ioctl to scrub AG metadata

Quoted 29 lines · Brian Foster
Quoted 17 lines · Darrick J. Wong

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

Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.

---
 fs/xfs/Kconfig           |   17 ++++++++++++++
 fs/xfs/Makefile          |   11 +++++++++
 fs/xfs/libxfs/xfs_fs.h   |   53 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h     |   25 +++++++++++++++++++++
 fs/xfs/scrub/trace.c     |   41 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h     |   33 ++++++++++++++++++++++++++++
 fs/xfs/scrub/xfs_scrub.h |   29 +++++++++++++++++++++++++
Quoted 10 lines
 fs/xfs/xfs_ioctl.c       |   28 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c     |    1 +
 10 files changed, 292 insertions(+)
 create mode 100644 fs/xfs/scrub/scrub.c
 create mode 100644 fs/xfs/scrub/scrub.h
 create mode 100644 fs/xfs/scrub/trace.c
 create mode 100644 fs/xfs/scrub/trace.h
 create mode 100644 fs/xfs/scrub/xfs_scrub.h
Quoted 2 lines

The code looks sane, though I think I need to understand the error codes a bit better. Perhaps once I get further into the series..

Yes, the ioctl needs documentation, though I'm unclear on where's an appropriate place to put them. The ioctl is not intended for general consumption, so man-pages.git seems inappropriate. I was thinking either in xfs_fs.h directly, or perhaps xfsprogs' man pages?

(We don't seem to document the xfs ioctls afaict?)

--D

Quoted 407 lines

Brian

Quoted 397 lines
fs/xfs/Kconfig +17 -0
index 1b98cfa..f42fcf1 100644
@@ -71,6 +71,23 @@ config XFS_RT
 
 	  If unsure, say N.
 
+config XFS_ONLINE_SCRUB
+	bool "XFS online metadata check support"
+	default n
+	depends on XFS_FS
+	help
+	  If you say Y here you will be able to check metadata on a
+	  mounted XFS filesystem.  This feature is intended to reduce
+	  filesystem downtime by supplementing xfs_repair.  The key
+	  advantage here is to look for problems proactively so that
+	  they can be dealt with in a controlled manner.
+
+	  This feature is considered EXPERIMENTAL.  Use with caution!
+
+	  See the xfs_scrub man page in section 8 for additional information.
+
+	  If unsure, say N.
+
 config XFS_WARN
 	bool "XFS Verbose Warnings"
 	depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
index dbc33e0..f4312bc 100644
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+
+# online scrub/repair
+ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
+
+# Tracepoints like to blow up, so build that before everything else
+
+xfs-y				+= $(addprefix scrub/, \
+				   trace.o \
+				   scrub.o \
+				   )
+endif
fs/xfs/libxfs/xfs_fs.h +53 -0
index 2c26c38..a4b4c8c 100644
@@ -468,6 +468,58 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
+/* metadata scrubbing */
+struct xfs_scrub_metadata {
+	__u32 sm_type;		/* What to check? */
+	__u32 sm_flags;		/* flags; see below. */
+	__u64 sm_ino;		/* inode number. */
+	__u32 sm_gen;		/* inode generation. */
+	__u32 sm_agno;		/* ag number. */
+	__u64 sm_reserved[5];	/* pad to 64 bytes */
+};
+
+/*
+ * Metadata types and flags for scrub operation.
+ */
+
+/* Scrub subcommands. */
+
+/* Number of scrub subcommands. */
+#define XFS_SCRUB_TYPE_NR	0
+
+/* i: Repair this metadata. */
+#define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
+
+/* o: Metadata object needs repair. */
+#define XFS_SCRUB_OFLAG_CORRUPT		(1 << 1)
+
+/*
+ * o: Metadata object could be optimized.  It's not corrupt, but
+ *    we could improve on it somehow.
+ */
+#define XFS_SCRUB_OFLAG_PREEN		(1 << 2)
+
+/* o: Cross-referencing failed. */
+#define XFS_SCRUB_OFLAG_XFAIL		(1 << 3)
+
+/* o: Metadata object disagrees with cross-referenced metadata. */
+#define XFS_SCRUB_OFLAG_XCORRUPT	(1 << 4)
+
+/* o: Scan was not complete. */
+#define XFS_SCRUB_OFLAG_INCOMPLETE	(1 << 5)
+
+/* o: Metadata object looked funny but isn't corrupt. */
+#define XFS_SCRUB_OFLAG_WARNING		(1 << 6)
+
+#define XFS_SCRUB_FLAGS_IN	(XFS_SCRUB_IFLAG_REPAIR)
+#define XFS_SCRUB_FLAGS_OUT	(XFS_SCRUB_OFLAG_CORRUPT | \
+				 XFS_SCRUB_OFLAG_PREEN | \
+				 XFS_SCRUB_OFLAG_XFAIL | \
+				 XFS_SCRUB_OFLAG_XCORRUPT | \
+				 XFS_SCRUB_OFLAG_INCOMPLETE | \
+				 XFS_SCRUB_OFLAG_WARNING)
+#define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+
 /*
  * AG reserved block counters
  */
@@ -522,6 +574,7 @@ struct xfs_fsop_ag_resblks {
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
 /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
+#define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/scrub/scrub.c new file +54 -0
new file mode 100644
index 0000000..5db2a6f
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/trace.h"
+
+/* Dispatch metadata scrubbing. */
+int
+xfs_scrub_metadata(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	return -EOPNOTSUPP;
+}
fs/xfs/scrub/scrub.h new file +25 -0
new file mode 100644
index 0000000..eb1cd9d
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_SCRUB_H__
+#define __XFS_SCRUB_SCRUB_H__
+
+/* Metadata scrubbers */
+
+#endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0
new file mode 100644
index 0000000..c59fd41
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_trans.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+
+/*
+ * We include this last to have the helpers above available for the trace
+ * event implementations.
+ */
+#define CREATE_TRACE_POINTS
+#include "scrub/trace.h"
fs/xfs/scrub/trace.h new file +33 -0
new file mode 100644
index 0000000..a95a7c8
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xfs_scrub
+
+#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_XFS_SCRUB_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#endif /* _TRACE_XFS_SCRUB_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE scrub/trace
+#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
new file mode 100644
index 0000000..e00e0ea
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_H__
+#define __XFS_SCRUB_H__
+
+#ifndef CONFIG_XFS_ONLINE_SCRUB
+# define xfs_scrub_metadata(ip, sm)	(-ENOTTY)
+#else
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
+#endif /* CONFIG_XFS_ONLINE_SCRUB */
+
+#endif	/* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
index 44dc178..ab7a7f8 100644
@@ -44,6 +44,7 @@
 #include "xfs_btree.h"
 #include <linux/fsmap.h>
 #include "xfs_fsmap.h"
+#include "scrub/xfs_scrub.h"
 
 #include <linux/capability.h>
 #include <linux/cred.h>
@@ -1702,6 +1703,30 @@ xfs_ioc_getfsmap(
 	return 0;
 }
 
+STATIC int
+xfs_ioc_scrub_metadata(
+	struct xfs_inode		*ip,
+	void				__user *arg)
+{
+	struct xfs_scrub_metadata	scrub;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&scrub, arg, sizeof(scrub)))
+		return -EFAULT;
+
+	error = xfs_scrub_metadata(ip, &scrub);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &scrub, sizeof(scrub)))
+		return -EFAULT;
+
+	return 0;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1906,6 +1931,9 @@ xfs_file_ioctl(
 	case FS_IOC_GETFSMAP:
 		return xfs_ioc_getfsmap(ip, arg);
 
+	case XFS_IOC_SCRUB_METADATA:
+		return xfs_ioc_scrub_metadata(ip, arg);
+
 	case XFS_IOC_FD_TO_HANDLE:
 	case XFS_IOC_PATH_TO_HANDLE:
 	case XFS_IOC_PATH_TO_FSHANDLE: {
fs/xfs/xfs_ioctl32.c +1 -1
index e8b4de3..972d4bd 100644
@@ -557,6 +557,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
 	case XFS_IOC_GET_AG_RESBLKS:
+	case XFS_IOC_SCRUB_METADATA:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
 
--
Quoted 3 lines

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

#9 lore raw

Re: [PATCH 03/27] xfs: create an ioctl to scrub AG metadata

Quoted 30 lines · Darrick J. Wong
Quoted 29 lines · Brian Foster
Quoted 27 lines · Darrick J. Wong

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

Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.

---
 fs/xfs/Kconfig           |   17 ++++++++++++++
 fs/xfs/Makefile          |   11 +++++++++
 fs/xfs/libxfs/xfs_fs.h   |   53 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h     |   25 +++++++++++++++++++++
 fs/xfs/scrub/trace.c     |   41 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h     |   33 ++++++++++++++++++++++++++++
 fs/xfs/scrub/xfs_scrub.h |   29 +++++++++++++++++++++++++
 fs/xfs/xfs_ioctl.c       |   28 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c     |    1 +
 10 files changed, 292 insertions(+)
 create mode 100644 fs/xfs/scrub/scrub.c
 create mode 100644 fs/xfs/scrub/scrub.h
 create mode 100644 fs/xfs/scrub/trace.c
 create mode 100644 fs/xfs/scrub/trace.h
 create mode 100644 fs/xfs/scrub/xfs_scrub.h
Quoted 8 lines
Quoted 2 lines

The code looks sane, though I think I need to understand the error codes a bit better. Perhaps once I get further into the series..

Yes, the ioctl needs documentation, though I'm unclear on where's an appropriate place to put them. The ioctl is not intended for general consumption, so man-pages.git seems inappropriate. I was thinking either in xfs_fs.h directly, or perhaps xfsprogs' man pages?

For now (while all of this is still experimental and until you get a better answer :P), it seems reasonable enough to me to add basic interface documentation to the kernel source. Then we at least have the content and can move it out into a manpage or something later, if needed.

Brian

Quoted 415 lines

(We don't seem to document the xfs ioctls afaict?)

--D

Quoted 399 lines

Brian

Quoted 396 lines
fs/xfs/Kconfig +17 -0
index 1b98cfa..f42fcf1 100644
@@ -71,6 +71,23 @@ config XFS_RT
 
 	  If unsure, say N.
 
+config XFS_ONLINE_SCRUB
+	bool "XFS online metadata check support"
+	default n
+	depends on XFS_FS
+	help
+	  If you say Y here you will be able to check metadata on a
+	  mounted XFS filesystem.  This feature is intended to reduce
+	  filesystem downtime by supplementing xfs_repair.  The key
+	  advantage here is to look for problems proactively so that
+	  they can be dealt with in a controlled manner.
+
+	  This feature is considered EXPERIMENTAL.  Use with caution!
+
+	  See the xfs_scrub man page in section 8 for additional information.
+
+	  If unsure, say N.
+
 config XFS_WARN
 	bool "XFS Verbose Warnings"
 	depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
index dbc33e0..f4312bc 100644
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+
+# online scrub/repair
+ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
+
+# Tracepoints like to blow up, so build that before everything else
+
+xfs-y				+= $(addprefix scrub/, \
+				   trace.o \
+				   scrub.o \
+				   )
+endif
fs/xfs/libxfs/xfs_fs.h +53 -0
index 2c26c38..a4b4c8c 100644
@@ -468,6 +468,58 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
+/* metadata scrubbing */
+struct xfs_scrub_metadata {
+	__u32 sm_type;		/* What to check? */
+	__u32 sm_flags;		/* flags; see below. */
+	__u64 sm_ino;		/* inode number. */
+	__u32 sm_gen;		/* inode generation. */
+	__u32 sm_agno;		/* ag number. */
+	__u64 sm_reserved[5];	/* pad to 64 bytes */
+};
+
+/*
+ * Metadata types and flags for scrub operation.
+ */
+
+/* Scrub subcommands. */
+
+/* Number of scrub subcommands. */
+#define XFS_SCRUB_TYPE_NR	0
+
+/* i: Repair this metadata. */
+#define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
+
+/* o: Metadata object needs repair. */
+#define XFS_SCRUB_OFLAG_CORRUPT		(1 << 1)
+
+/*
+ * o: Metadata object could be optimized.  It's not corrupt, but
+ *    we could improve on it somehow.
+ */
+#define XFS_SCRUB_OFLAG_PREEN		(1 << 2)
+
+/* o: Cross-referencing failed. */
+#define XFS_SCRUB_OFLAG_XFAIL		(1 << 3)
+
+/* o: Metadata object disagrees with cross-referenced metadata. */
+#define XFS_SCRUB_OFLAG_XCORRUPT	(1 << 4)
+
+/* o: Scan was not complete. */
+#define XFS_SCRUB_OFLAG_INCOMPLETE	(1 << 5)
+
+/* o: Metadata object looked funny but isn't corrupt. */
+#define XFS_SCRUB_OFLAG_WARNING		(1 << 6)
+
+#define XFS_SCRUB_FLAGS_IN	(XFS_SCRUB_IFLAG_REPAIR)
+#define XFS_SCRUB_FLAGS_OUT	(XFS_SCRUB_OFLAG_CORRUPT | \
+				 XFS_SCRUB_OFLAG_PREEN | \
+				 XFS_SCRUB_OFLAG_XFAIL | \
+				 XFS_SCRUB_OFLAG_XCORRUPT | \
+				 XFS_SCRUB_OFLAG_INCOMPLETE | \
+				 XFS_SCRUB_OFLAG_WARNING)
+#define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+
 /*
  * AG reserved block counters
  */
@@ -522,6 +574,7 @@ struct xfs_fsop_ag_resblks {
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
 /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
+#define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/scrub/scrub.c new file +54 -0
new file mode 100644
index 0000000..5db2a6f
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/trace.h"
+
+/* Dispatch metadata scrubbing. */
+int
+xfs_scrub_metadata(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	return -EOPNOTSUPP;
+}
fs/xfs/scrub/scrub.h new file +25 -0
new file mode 100644
index 0000000..eb1cd9d
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_SCRUB_H__
+#define __XFS_SCRUB_SCRUB_H__
+
+/* Metadata scrubbers */
+
+#endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0
new file mode 100644
index 0000000..c59fd41
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_trans.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+
+/*
+ * We include this last to have the helpers above available for the trace
+ * event implementations.
+ */
+#define CREATE_TRACE_POINTS
+#include "scrub/trace.h"
fs/xfs/scrub/trace.h new file +33 -0
new file mode 100644
index 0000000..a95a7c8
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xfs_scrub
+
+#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_XFS_SCRUB_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#endif /* _TRACE_XFS_SCRUB_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE scrub/trace
+#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
new file mode 100644
index 0000000..e00e0ea
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_H__
+#define __XFS_SCRUB_H__
+
+#ifndef CONFIG_XFS_ONLINE_SCRUB
+# define xfs_scrub_metadata(ip, sm)	(-ENOTTY)
+#else
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
+#endif /* CONFIG_XFS_ONLINE_SCRUB */
+
+#endif	/* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
index 44dc178..ab7a7f8 100644
@@ -44,6 +44,7 @@
 #include "xfs_btree.h"
 #include <linux/fsmap.h>
 #include "xfs_fsmap.h"
+#include "scrub/xfs_scrub.h"
 
 #include <linux/capability.h>
 #include <linux/cred.h>
@@ -1702,6 +1703,30 @@ xfs_ioc_getfsmap(
 	return 0;
 }
 
+STATIC int
+xfs_ioc_scrub_metadata(
+	struct xfs_inode		*ip,
+	void				__user *arg)
+{
+	struct xfs_scrub_metadata	scrub;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&scrub, arg, sizeof(scrub)))
+		return -EFAULT;
+
+	error = xfs_scrub_metadata(ip, &scrub);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &scrub, sizeof(scrub)))
+		return -EFAULT;
+
+	return 0;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1906,6 +1931,9 @@ xfs_file_ioctl(
 	case FS_IOC_GETFSMAP:
 		return xfs_ioc_getfsmap(ip, arg);
 
+	case XFS_IOC_SCRUB_METADATA:
+		return xfs_ioc_scrub_metadata(ip, arg);
+
 	case XFS_IOC_FD_TO_HANDLE:
 	case XFS_IOC_PATH_TO_HANDLE:
 	case XFS_IOC_PATH_TO_FSHANDLE: {
fs/xfs/xfs_ioctl32.c +1 -0
index e8b4de3..972d4bd 100644
@@ -557,6 +557,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
 	case XFS_IOC_GET_AG_RESBLKS:
+	case XFS_IOC_SCRUB_METADATA:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
Quoted 8 lines
Quoted 4 lines

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

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

#10 lore raw

Re: [PATCH 03/27] xfs: create an ioctl to scrub AG metadata

Quoted 40 lines · Brian Foster
Quoted 30 lines · Darrick J. Wong
Quoted 29 lines · Brian Foster
Quoted 27 lines · Darrick J. Wong

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

Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.

---
 fs/xfs/Kconfig           |   17 ++++++++++++++
 fs/xfs/Makefile          |   11 +++++++++
 fs/xfs/libxfs/xfs_fs.h   |   53 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c     |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h     |   25 +++++++++++++++++++++
 fs/xfs/scrub/trace.c     |   41 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h     |   33 ++++++++++++++++++++++++++++
 fs/xfs/scrub/xfs_scrub.h |   29 +++++++++++++++++++++++++
 fs/xfs/xfs_ioctl.c       |   28 ++++++++++++++++++++++++
 fs/xfs/xfs_ioctl32.c     |    1 +
 10 files changed, 292 insertions(+)
 create mode 100644 fs/xfs/scrub/scrub.c
 create mode 100644 fs/xfs/scrub/scrub.h
 create mode 100644 fs/xfs/scrub/trace.c
 create mode 100644 fs/xfs/scrub/trace.h
 create mode 100644 fs/xfs/scrub/xfs_scrub.h
Quoted 8 lines
Quoted 2 lines

The code looks sane, though I think I need to understand the error codes a bit better. Perhaps once I get further into the series..

Yes, the ioctl needs documentation, though I'm unclear on where's an appropriate place to put them. The ioctl is not intended for general consumption, so man-pages.git seems inappropriate. I was thinking either in xfs_fs.h directly, or perhaps xfsprogs' man pages?

Quoted 5 lines

For now (while all of this is still experimental and until you get a better answer :P), it seems reasonable enough to me to add basic interface documentation to the kernel source. Then we at least have the content and can move it out into a manpage or something later, if needed.

It's more or less documented in the source... but in a somewhat piecemeal fashion as we add functionality. I've drafted a manpage to describe all of what the ioctl wants to do, so I'll tack that on the end of this series (and cc you and Dave).

--D

Quoted 422 lines

Brian

Quoted 411 lines

(We don't seem to document the xfs ioctls afaict?)

--D

Quoted 399 lines

Brian

Quoted 396 lines
fs/xfs/Kconfig +17 -0
index 1b98cfa..f42fcf1 100644
@@ -71,6 +71,23 @@ config XFS_RT
 
 	  If unsure, say N.
 
+config XFS_ONLINE_SCRUB
+	bool "XFS online metadata check support"
+	default n
+	depends on XFS_FS
+	help
+	  If you say Y here you will be able to check metadata on a
+	  mounted XFS filesystem.  This feature is intended to reduce
+	  filesystem downtime by supplementing xfs_repair.  The key
+	  advantage here is to look for problems proactively so that
+	  they can be dealt with in a controlled manner.
+
+	  This feature is considered EXPERIMENTAL.  Use with caution!
+
+	  See the xfs_scrub man page in section 8 for additional information.
+
+	  If unsure, say N.
+
 config XFS_WARN
 	bool "XFS Verbose Warnings"
 	depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
index dbc33e0..f4312bc 100644
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL)	+= xfs_acl.o
 xfs-$(CONFIG_SYSCTL)		+= xfs_sysctl.o
 xfs-$(CONFIG_COMPAT)		+= xfs_ioctl32.o
 xfs-$(CONFIG_EXPORTFS_BLOCK_OPS)	+= xfs_pnfs.o
+
+# online scrub/repair
+ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
+
+# Tracepoints like to blow up, so build that before everything else
+
+xfs-y				+= $(addprefix scrub/, \
+				   trace.o \
+				   scrub.o \
+				   )
+endif
fs/xfs/libxfs/xfs_fs.h +53 -0
index 2c26c38..a4b4c8c 100644
@@ -468,6 +468,58 @@ typedef struct xfs_swapext
 #define XFS_FSOP_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
 #define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
 
+/* metadata scrubbing */
+struct xfs_scrub_metadata {
+	__u32 sm_type;		/* What to check? */
+	__u32 sm_flags;		/* flags; see below. */
+	__u64 sm_ino;		/* inode number. */
+	__u32 sm_gen;		/* inode generation. */
+	__u32 sm_agno;		/* ag number. */
+	__u64 sm_reserved[5];	/* pad to 64 bytes */
+};
+
+/*
+ * Metadata types and flags for scrub operation.
+ */
+
+/* Scrub subcommands. */
+
+/* Number of scrub subcommands. */
+#define XFS_SCRUB_TYPE_NR	0
+
+/* i: Repair this metadata. */
+#define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
+
+/* o: Metadata object needs repair. */
+#define XFS_SCRUB_OFLAG_CORRUPT		(1 << 1)
+
+/*
+ * o: Metadata object could be optimized.  It's not corrupt, but
+ *    we could improve on it somehow.
+ */
+#define XFS_SCRUB_OFLAG_PREEN		(1 << 2)
+
+/* o: Cross-referencing failed. */
+#define XFS_SCRUB_OFLAG_XFAIL		(1 << 3)
+
+/* o: Metadata object disagrees with cross-referenced metadata. */
+#define XFS_SCRUB_OFLAG_XCORRUPT	(1 << 4)
+
+/* o: Scan was not complete. */
+#define XFS_SCRUB_OFLAG_INCOMPLETE	(1 << 5)
+
+/* o: Metadata object looked funny but isn't corrupt. */
+#define XFS_SCRUB_OFLAG_WARNING		(1 << 6)
+
+#define XFS_SCRUB_FLAGS_IN	(XFS_SCRUB_IFLAG_REPAIR)
+#define XFS_SCRUB_FLAGS_OUT	(XFS_SCRUB_OFLAG_CORRUPT | \
+				 XFS_SCRUB_OFLAG_PREEN | \
+				 XFS_SCRUB_OFLAG_XFAIL | \
+				 XFS_SCRUB_OFLAG_XCORRUPT | \
+				 XFS_SCRUB_OFLAG_INCOMPLETE | \
+				 XFS_SCRUB_OFLAG_WARNING)
+#define XFS_SCRUB_FLAGS_ALL	(XFS_SCRUB_FLAGS_IN | XFS_SCRUB_FLAGS_OUT)
+
 /*
  * AG reserved block counters
  */
@@ -522,6 +574,7 @@ struct xfs_fsop_ag_resblks {
 #define XFS_IOC_ZERO_RANGE	_IOW ('X', 57, struct xfs_flock64)
 #define XFS_IOC_FREE_EOFBLOCKS	_IOR ('X', 58, struct xfs_fs_eofblocks)
 /*	XFS_IOC_GETFSMAP ------ hoisted 59         */
+#define XFS_IOC_SCRUB_METADATA	_IOWR('X', 60, struct xfs_scrub_metadata)
 
 /*
  * ioctl commands that replace IRIX syssgi()'s
fs/xfs/scrub/scrub.c new file +54 -0
new file mode 100644
index 0000000..5db2a6f
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/trace.h"
+
+/* Dispatch metadata scrubbing. */
+int
+xfs_scrub_metadata(
+	struct xfs_inode		*ip,
+	struct xfs_scrub_metadata	*sm)
+{
+	return -EOPNOTSUPP;
+}
fs/xfs/scrub/scrub.h new file +25 -0
new file mode 100644
index 0000000..eb1cd9d
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_SCRUB_H__
+#define __XFS_SCRUB_SCRUB_H__
+
+/* Metadata scrubbers */
+
+#endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0
new file mode 100644
index 0000000..c59fd41
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_trans.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+
+/*
+ * We include this last to have the helpers above available for the trace
+ * event implementations.
+ */
+#define CREATE_TRACE_POINTS
+#include "scrub/trace.h"
fs/xfs/scrub/trace.h new file +33 -0
new file mode 100644
index 0000000..a95a7c8
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM xfs_scrub
+
+#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_XFS_SCRUB_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#endif /* _TRACE_XFS_SCRUB_TRACE_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE scrub/trace
+#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
new file mode 100644
index 0000000..e00e0ea
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_H__
+#define __XFS_SCRUB_H__
+
+#ifndef CONFIG_XFS_ONLINE_SCRUB
+# define xfs_scrub_metadata(ip, sm)	(-ENOTTY)
+#else
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
+#endif /* CONFIG_XFS_ONLINE_SCRUB */
+
+#endif	/* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
index 44dc178..ab7a7f8 100644
@@ -44,6 +44,7 @@
 #include "xfs_btree.h"
 #include <linux/fsmap.h>
 #include "xfs_fsmap.h"
+#include "scrub/xfs_scrub.h"
 
 #include <linux/capability.h>
 #include <linux/cred.h>
@@ -1702,6 +1703,30 @@ xfs_ioc_getfsmap(
 	return 0;
 }
 
+STATIC int
+xfs_ioc_scrub_metadata(
+	struct xfs_inode		*ip,
+	void				__user *arg)
+{
+	struct xfs_scrub_metadata	scrub;
+	int				error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&scrub, arg, sizeof(scrub)))
+		return -EFAULT;
+
+	error = xfs_scrub_metadata(ip, &scrub);
+	if (error)
+		return error;
+
+	if (copy_to_user(arg, &scrub, sizeof(scrub)))
+		return -EFAULT;
+
+	return 0;
+}
+
 int
 xfs_ioc_swapext(
 	xfs_swapext_t	*sxp)
@@ -1906,6 +1931,9 @@ xfs_file_ioctl(
 	case FS_IOC_GETFSMAP:
 		return xfs_ioc_getfsmap(ip, arg);
 
+	case XFS_IOC_SCRUB_METADATA:
+		return xfs_ioc_scrub_metadata(ip, arg);
+
 	case XFS_IOC_FD_TO_HANDLE:
 	case XFS_IOC_PATH_TO_HANDLE:
 	case XFS_IOC_PATH_TO_FSHANDLE: {
fs/xfs/xfs_ioctl32.c +1 -0
index e8b4de3..972d4bd 100644
@@ -557,6 +557,7 @@ xfs_file_compat_ioctl(
 	case XFS_IOC_ERROR_CLEARALL:
 	case FS_IOC_GETFSMAP:
 	case XFS_IOC_GET_AG_RESBLKS:
+	case XFS_IOC_SCRUB_METADATA:
 		return xfs_file_ioctl(filp, cmd, p);
 #ifndef BROKEN_X86_ALIGNMENT
 	/* These are handled fine if no alignment issues */
Quoted 8 lines
Quoted 4 lines

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

Quoted 4 lines

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

#11 lore raw

PATCH v10 4/27 xfs: dispatch metadata scrub subcommands

In reply to Darrick J. Wong

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

Create structures needed to hold scrubbing context and dispatch incoming commands to the individual scrubbers.

---
 fs/xfs/scrub/scrub.c |  172 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h |   19 ++++++
 fs/xfs/scrub/trace.h |   43 +++++++++++++
 3 files changed, 233 insertions(+), 1 deletion(-)
fs/xfs/scrub/scrub.c +171 -1
index 5db2a6f..7cf518e 100644
@@ -44,11 +44,181 @@
 #include "scrub/scrub.h"
 #include "scrub/trace.h"
 
+/*
+ * Online Scrub and Repair
+ *
+ * Traditionally, XFS (the kernel driver) did not know how to check or
+ * repair on-disk data structures.  That task was left to the xfs_check
+ * and xfs_repair tools, both of which require taking the filesystem
+ * offline for a thorough but time consuming examination.  Online
+ * scrub & repair, on the other hand, enables us to check the metadata
+ * for obvious errors while carefully stepping around the filesystem's
+ * ongoing operations, locking rules, etc.
+ *
+ * Given that most XFS metadata consist of records stored in a btree,
+ * most of the checking functions iterate the btree blocks themselves
+ * looking for irregularities.  When a record block is encountered, each
+ * record can be checked for obviously bad values.  Record values can
+ * also be cross-referenced against other btrees to look for potential
+ * misunderstandings between pieces of metadata.
+ *
+ * It is expected that the checkers responsible for per-AG metadata
+ * structures will lock the AG headers (AGI, AGF, AGFL), iterate the
+ * metadata structure, and perform any relevant cross-referencing before
+ * unlocking the AG and returning the results to userspace.  These
+ * scrubbers must not keep an AG locked for too long to avoid tying up
+ * the block and inode allocators.
+ *
+ * Block maps and b-trees rooted in an inode present a special challenge
+ * because they can involve extents from any AG.  The general scrubber
+ * structure of lock -> check -> xref -> unlock still holds, but AG
+ * locking order rules /must/ be obeyed to avoid deadlocks.  The
+ * ordering rule, of course, is that we must lock in increasing AG
+ * order.  Helper functions are provided to track which AG headers we've
+ * already locked.  If we detect an imminent locking order violation, we
+ * can signal a potential deadlock, in which case the scrubber can jump
+ * out to the top level, lock all the AGs in order, and retry the scrub.
+ *
+ * For file data (directories, extended attributes, symlinks) scrub, we
+ * can simply lock the inode and walk the data.  For btree data
+ * (directories and attributes) we follow the same btree-scrubbing
+ * strategy outlined previously to check the records.
+ *
+ * We use a bit of trickery with transactions to avoid buffer deadlocks
+ * if there is a cycle in the metadata.  The basic problem is that
+ * travelling down a btree involves locking the current buffer at each
+ * tree level.  If a pointer should somehow point back to a buffer that
+ * we've already examined, we will deadlock due to the second buffer
+ * locking attempt.  Note however that grabbing a buffer in transaction
+ * context links the locked buffer to the transaction.  If we try to
+ * re-grab the buffer in the context of the same transaction, we avoid
+ * the second lock attempt and continue.  Between the verifier and the
+ * scrubber, something will notice that something is amiss and report
+ * the corruption.  Therefore, each scrubber will allocate an empty
+ * transaction, attach buffers to it, and cancel the transaction at the
+ * end of the scrub run.  Cancelling a non-dirty transaction simply
+ * unlocks the buffers.
+ *
+ * There are four pieces of data that scrub can communicate to
+ * userspace.  The first is the error code (errno), which can be used to
+ * communicate operational errors in performing the scrub.  There are
+ * also three flags that can be set in the scrub context.  If the data
+ * structure itself is corrupt, the CORRUPT flag will be set.  If
+ * the metadata is correct but otherwise suboptimal, the PREEN flag
+ * will be set.
+ */
+
+/* Scrub setup and teardown */
+
+/* Free all the resources and finish the transactions. */
+STATIC int
+xfs_scrub_teardown(
+	struct xfs_scrub_context	*sc,
+	int				error)
+{
+	if (sc->tp) {
+		xfs_trans_cancel(sc->tp);
+		sc->tp = NULL;
+	}
+	return error;
+}
+
+/* Scrubbing dispatch. */
+
+static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
+};
+
 /* Dispatch metadata scrubbing. */
 int
 xfs_scrub_metadata(
 	struct xfs_inode		*ip,
 	struct xfs_scrub_metadata	*sm)
 {
-	return -EOPNOTSUPP;
+	struct xfs_scrub_context	sc;
+	struct xfs_mount		*mp = ip->i_mount;
+	const struct xfs_scrub_meta_ops	*ops;
+	bool				try_harder = false;
+	int				error = 0;
+
+	trace_xfs_scrub_start(ip, sm, error);
+
+	/* Forbidden if we are shut down or mounted norecovery. */
+	error = -ESHUTDOWN;
+	if (XFS_FORCED_SHUTDOWN(mp))
+		goto out;
+	error = -ENOTRECOVERABLE;
+	if (mp->m_flags & XFS_MOUNT_NORECOVERY)
+		goto out;
+
+	/* Check our inputs. */
+	error = -EINVAL;
+	sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
+	if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
+		goto out;
+	if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
+		goto out;
+
+	/* Do we know about this type of metadata? */
+	error = -ENOENT;
+	if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
+		goto out;
+	ops = &meta_scrub_ops[sm->sm_type];
+	if (ops->scrub == NULL)
+		goto out;
+
+	/* Does this fs even support this type of metadata? */
+	if (ops->has && !ops->has(&mp->m_sb))
+		goto out;
+
+	/* We don't know how to repair anything yet. */
+	error = -EOPNOTSUPP;
+	if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
+		goto out;
+
+	/* This isn't a stable feature.  Use with care. */
+	{
+		static bool warned;
+
+		if (!warned)
+			xfs_alert(mp,
+	"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
+		warned = true;
+	}
+
+retry_op:
+	/* Set up for the operation. */
+	memset(&sc, 0, sizeof(sc));
+	sc.mp = ip->i_mount;
+	sc.sm = sm;
+	sc.ops = ops;
+	sc.try_harder = try_harder;
+	error = sc.ops->setup(&sc, ip);
+	if (error)
+		goto out_teardown;
+
+	/* Scrub for errors. */
+	error = sc.ops->scrub(&sc);
+	if (!try_harder && error == -EDEADLOCK) {
+		/*
+		 * Scrubbers return -EDEADLOCK to mean 'try harder'.
+		 * Tear down everything we hold, then set up again with
+		 * preparation for worst-case scenarios.
+		 */
+		error = xfs_scrub_teardown(&sc, 0);
+		if (error)
+			goto out;
+		try_harder = true;
+		goto retry_op;
+	} else if (error)
+		goto out_teardown;
+
+	if (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
+			       XFS_SCRUB_OFLAG_XCORRUPT))
+		xfs_alert_ratelimited(mp, "Corruption detected during scrub.");
+
+out_teardown:
+	error = xfs_scrub_teardown(&sc, error);
+out:
+	trace_xfs_scrub_done(ip, sm, error);
+	return error;
 }
fs/xfs/scrub/scrub.h +19 -0
index eb1cd9d..b271b2a 100644
@@ -20,6 +20,25 @@
 #ifndef __XFS_SCRUB_SCRUB_H__
 #define __XFS_SCRUB_SCRUB_H__
 
+struct xfs_scrub_context;
+
+struct xfs_scrub_meta_ops {
+	int		(*setup)(struct xfs_scrub_context *,
+				 struct xfs_inode *);
+	int		(*scrub)(struct xfs_scrub_context *);
+	bool		(*has)(struct xfs_sb *);
+};
+
+struct xfs_scrub_context {
+	/* General scrub state. */
+	struct xfs_mount		*mp;
+	struct xfs_scrub_metadata	*sm;
+	const struct xfs_scrub_meta_ops	*ops;
+	struct xfs_trans		*tp;
+	struct xfs_inode		*ip;
+	bool				try_harder;
+};
+
 /* Metadata scrubbers */
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.h +43 -0

49 lines not on this page · raw message

#12 lore raw

Re: [PATCH 04/27] xfs: dispatch metadata scrub subcommands

Quoted 73 lines · Darrick J. Wong

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

Create structures needed to hold scrubbing context and dispatch incoming commands to the individual scrubbers.

---
 fs/xfs/scrub/scrub.c |  172 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h |   19 ++++++
 fs/xfs/scrub/trace.h |   43 +++++++++++++
 3 files changed, 233 insertions(+), 1 deletion(-)
fs/xfs/scrub/scrub.c +52 -0
index 5db2a6f..7cf518e 100644
@@ -44,11 +44,181 @@
 #include "scrub/scrub.h"
 #include "scrub/trace.h"
 
+/*
+ * Online Scrub and Repair
+ *
+ * Traditionally, XFS (the kernel driver) did not know how to check or
+ * repair on-disk data structures.  That task was left to the xfs_check
+ * and xfs_repair tools, both of which require taking the filesystem
+ * offline for a thorough but time consuming examination.  Online
+ * scrub & repair, on the other hand, enables us to check the metadata
+ * for obvious errors while carefully stepping around the filesystem's
+ * ongoing operations, locking rules, etc.
+ *
+ * Given that most XFS metadata consist of records stored in a btree,
+ * most of the checking functions iterate the btree blocks themselves
+ * looking for irregularities.  When a record block is encountered, each
+ * record can be checked for obviously bad values.  Record values can
+ * also be cross-referenced against other btrees to look for potential
+ * misunderstandings between pieces of metadata.
+ *
+ * It is expected that the checkers responsible for per-AG metadata
+ * structures will lock the AG headers (AGI, AGF, AGFL), iterate the
+ * metadata structure, and perform any relevant cross-referencing before
+ * unlocking the AG and returning the results to userspace.  These
+ * scrubbers must not keep an AG locked for too long to avoid tying up
+ * the block and inode allocators.
+ *
+ * Block maps and b-trees rooted in an inode present a special challenge
+ * because they can involve extents from any AG.  The general scrubber
+ * structure of lock -> check -> xref -> unlock still holds, but AG
+ * locking order rules /must/ be obeyed to avoid deadlocks.  The
+ * ordering rule, of course, is that we must lock in increasing AG
+ * order.  Helper functions are provided to track which AG headers we've
+ * already locked.  If we detect an imminent locking order violation, we
+ * can signal a potential deadlock, in which case the scrubber can jump
+ * out to the top level, lock all the AGs in order, and retry the scrub.
+ *
+ * For file data (directories, extended attributes, symlinks) scrub, we
+ * can simply lock the inode and walk the data.  For btree data
+ * (directories and attributes) we follow the same btree-scrubbing
+ * strategy outlined previously to check the records.
+ *
+ * We use a bit of trickery with transactions to avoid buffer deadlocks
+ * if there is a cycle in the metadata.  The basic problem is that
+ * travelling down a btree involves locking the current buffer at each
+ * tree level.  If a pointer should somehow point back to a buffer that
+ * we've already examined, we will deadlock due to the second buffer
+ * locking attempt.  Note however that grabbing a buffer in transaction
+ * context links the locked buffer to the transaction.  If we try to
+ * re-grab the buffer in the context of the same transaction, we avoid
+ * the second lock attempt and continue.  Between the verifier and the
+ * scrubber, something will notice that something is amiss and report
+ * the corruption.  Therefore, each scrubber will allocate an empty
+ * transaction, attach buffers to it, and cancel the transaction at the

fs/xfs/scrub/scrub.c line 108

Quoted 10 lines
fs/xfs/scrub/scrub.c +10 -0
+ * end of the scrub run.  Cancelling a non-dirty transaction simply
+ * unlocks the buffers.
+ *
+ * There are four pieces of data that scrub can communicate to
+ * userspace.  The first is the error code (errno), which can be used to
+ * communicate operational errors in performing the scrub.  There are
+ * also three flags that can be set in the scrub context.  If the data
+ * structure itself is corrupt, the CORRUPT flag will be set.  If
+ * the metadata is correct but otherwise suboptimal, the PREEN flag
+ * will be set.

Did you mean to describe other flags here?

fs/xfs/scrub/scrub.c line 117

Quoted 9 lines
fs/xfs/scrub/scrub.c +9 -0
+ */
+
+/* Scrub setup and teardown */
+
+/* Free all the resources and finish the transactions. */
+STATIC int
+xfs_scrub_teardown(
+	struct xfs_scrub_context	*sc,
+	int				error)

What's the purpose of passing error just to return it?

Quoted 113 lines
fs/xfs/scrub/scrub.c +100 -1
+{
+	if (sc->tp) {
+		xfs_trans_cancel(sc->tp);
+		sc->tp = NULL;
+	}
+	return error;
+}
+
+/* Scrubbing dispatch. */
+
+static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
+};
+
 /* Dispatch metadata scrubbing. */
 int
 xfs_scrub_metadata(
 	struct xfs_inode		*ip,
 	struct xfs_scrub_metadata	*sm)
 {
-	return -EOPNOTSUPP;
+	struct xfs_scrub_context	sc;
+	struct xfs_mount		*mp = ip->i_mount;
+	const struct xfs_scrub_meta_ops	*ops;
+	bool				try_harder = false;
+	int				error = 0;
+
+	trace_xfs_scrub_start(ip, sm, error);
+
+	/* Forbidden if we are shut down or mounted norecovery. */
+	error = -ESHUTDOWN;
+	if (XFS_FORCED_SHUTDOWN(mp))
+		goto out;
+	error = -ENOTRECOVERABLE;
+	if (mp->m_flags & XFS_MOUNT_NORECOVERY)
+		goto out;
+
+	/* Check our inputs. */
+	error = -EINVAL;
+	sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
+	if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
+		goto out;
+	if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
+		goto out;
+
+	/* Do we know about this type of metadata? */
+	error = -ENOENT;
+	if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
+		goto out;
+	ops = &meta_scrub_ops[sm->sm_type];
+	if (ops->scrub == NULL)
+		goto out;
+
+	/* Does this fs even support this type of metadata? */
+	if (ops->has && !ops->has(&mp->m_sb))
+		goto out;
+
+	/* We don't know how to repair anything yet. */
+	error = -EOPNOTSUPP;
+	if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
+		goto out;
+
+	/* This isn't a stable feature.  Use with care. */
+	{
+		static bool warned;
+
+		if (!warned)
+			xfs_alert(mp,
+	"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
+		warned = true;
+	}
+
+retry_op:
+	/* Set up for the operation. */
+	memset(&sc, 0, sizeof(sc));
+	sc.mp = ip->i_mount;
+	sc.sm = sm;
+	sc.ops = ops;
+	sc.try_harder = try_harder;
+	error = sc.ops->setup(&sc, ip);
+	if (error)
+		goto out_teardown;
+
+	/* Scrub for errors. */
+	error = sc.ops->scrub(&sc);
+	if (!try_harder && error == -EDEADLOCK) {
+		/*
+		 * Scrubbers return -EDEADLOCK to mean 'try harder'.
+		 * Tear down everything we hold, then set up again with
+		 * preparation for worst-case scenarios.
+		 */
+		error = xfs_scrub_teardown(&sc, 0);
+		if (error)
+			goto out;
+		try_harder = true;
+		goto retry_op;
+	} else if (error)
+		goto out_teardown;
+
+	if (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
+			       XFS_SCRUB_OFLAG_XCORRUPT))
+		xfs_alert_ratelimited(mp, "Corruption detected during scrub.");
+
+out_teardown:
+	error = xfs_scrub_teardown(&sc, error);
+out:
+	trace_xfs_scrub_done(ip, sm, error);
+	return error;
 }
fs/xfs/scrub/scrub.h +0 -0
index eb1cd9d..b271b2a 100644

fs/xfs/scrub/scrub.h line 29

Quoted 10 lines
fs/xfs/scrub/scrub.h +7 -0
@@ -20,6 +20,25 @@
 #ifndef __XFS_SCRUB_SCRUB_H__
 #define __XFS_SCRUB_SCRUB_H__
 
+struct xfs_scrub_context;
+
+struct xfs_scrub_meta_ops {
+	int		(*setup)(struct xfs_scrub_context *,
+				 struct xfs_inode *);
+	int		(*scrub)(struct xfs_scrub_context *);
+	bool		(*has)(struct xfs_sb *);

I assume 'has' is to identify whether a particular mount supports a particular feature. I suppose a better name would be nice here, or perhaps just a comment to outline the purpose of each callout.

Brian

Quoted 74 lines
fs/xfs/scrub/scrub.h +12 -0
+};
+
+struct xfs_scrub_context {
+	/* General scrub state. */
+	struct xfs_mount		*mp;
+	struct xfs_scrub_metadata	*sm;
+	const struct xfs_scrub_meta_ops	*ops;
+	struct xfs_trans		*tp;
+	struct xfs_inode		*ip;
+	bool				try_harder;
+};
+
 /* Metadata scrubbers */
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.h +43 -1
index a95a7c8..688517e 100644
@@ -25,6 +25,49 @@
 
 #include <linux/tracepoint.h>
 
+DECLARE_EVENT_CLASS(xfs_scrub_class,
+	TP_PROTO(struct xfs_inode *ip, struct xfs_scrub_metadata *sm,
+		 int error),
+	TP_ARGS(ip, sm, error),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(xfs_ino_t, ino)
+		__field(unsigned int, type)
+		__field(xfs_agnumber_t, agno)
+		__field(xfs_ino_t, inum)
+		__field(unsigned int, gen)
+		__field(unsigned int, flags)
+		__field(int, error)
+	),
+	TP_fast_assign(
+		__entry->dev = ip->i_mount->m_super->s_dev;
+		__entry->ino = ip->i_ino;
+		__entry->type = sm->sm_type;
+		__entry->agno = sm->sm_agno;
+		__entry->inum = sm->sm_ino;
+		__entry->gen = sm->sm_gen;
+		__entry->flags = sm->sm_flags;
+		__entry->error = error;
+	),
+	TP_printk("dev %d:%d ino %llu type %u agno %u inum %llu gen %u flags 0x%x error %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->ino,
+		  __entry->type,
+		  __entry->agno,
+		  __entry->inum,
+		  __entry->gen,
+		  __entry->flags,
+		  __entry->error)
+)
+#define DEFINE_SCRUB_EVENT(name) \
+DEFINE_EVENT(xfs_scrub_class, name, \
+	TP_PROTO(struct xfs_inode *ip, struct xfs_scrub_metadata *sm, \
+		 int error), \
+	TP_ARGS(ip, sm, error))
+
+DEFINE_SCRUB_EVENT(xfs_scrub_start);
+DEFINE_SCRUB_EVENT(xfs_scrub_done);
+
 #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

#13 lore raw

Re: [PATCH 04/27] xfs: dispatch metadata scrub subcommands

Quoted 78 lines · Brian Foster
Quoted 77 lines · Darrick J. Wong

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

Create structures needed to hold scrubbing context and dispatch incoming commands to the individual scrubbers.

---
 fs/xfs/scrub/scrub.c |  172 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h |   19 ++++++
 fs/xfs/scrub/trace.h |   43 +++++++++++++
 3 files changed, 233 insertions(+), 1 deletion(-)
fs/xfs/scrub/scrub.c +56 -0
index 5db2a6f..7cf518e 100644
@@ -44,11 +44,181 @@
 #include "scrub/scrub.h"
 #include "scrub/trace.h"
 
+/*
+ * Online Scrub and Repair
+ *
+ * Traditionally, XFS (the kernel driver) did not know how to check or
+ * repair on-disk data structures.  That task was left to the xfs_check
+ * and xfs_repair tools, both of which require taking the filesystem
+ * offline for a thorough but time consuming examination.  Online
+ * scrub & repair, on the other hand, enables us to check the metadata
+ * for obvious errors while carefully stepping around the filesystem's
+ * ongoing operations, locking rules, etc.
+ *
+ * Given that most XFS metadata consist of records stored in a btree,
+ * most of the checking functions iterate the btree blocks themselves
+ * looking for irregularities.  When a record block is encountered, each
+ * record can be checked for obviously bad values.  Record values can
+ * also be cross-referenced against other btrees to look for potential
+ * misunderstandings between pieces of metadata.
+ *
+ * It is expected that the checkers responsible for per-AG metadata
+ * structures will lock the AG headers (AGI, AGF, AGFL), iterate the
+ * metadata structure, and perform any relevant cross-referencing before
+ * unlocking the AG and returning the results to userspace.  These
+ * scrubbers must not keep an AG locked for too long to avoid tying up
+ * the block and inode allocators.
+ *
+ * Block maps and b-trees rooted in an inode present a special challenge
+ * because they can involve extents from any AG.  The general scrubber
+ * structure of lock -> check -> xref -> unlock still holds, but AG
+ * locking order rules /must/ be obeyed to avoid deadlocks.  The
+ * ordering rule, of course, is that we must lock in increasing AG
+ * order.  Helper functions are provided to track which AG headers we've
+ * already locked.  If we detect an imminent locking order violation, we
+ * can signal a potential deadlock, in which case the scrubber can jump
+ * out to the top level, lock all the AGs in order, and retry the scrub.
+ *
+ * For file data (directories, extended attributes, symlinks) scrub, we
+ * can simply lock the inode and walk the data.  For btree data
+ * (directories and attributes) we follow the same btree-scrubbing
+ * strategy outlined previously to check the records.
+ *
+ * We use a bit of trickery with transactions to avoid buffer deadlocks
+ * if there is a cycle in the metadata.  The basic problem is that
+ * travelling down a btree involves locking the current buffer at each
+ * tree level.  If a pointer should somehow point back to a buffer that
+ * we've already examined, we will deadlock due to the second buffer
+ * locking attempt.  Note however that grabbing a buffer in transaction
+ * context links the locked buffer to the transaction.  If we try to
+ * re-grab the buffer in the context of the same transaction, we avoid
+ * the second lock attempt and continue.  Between the verifier and the
+ * scrubber, something will notice that something is amiss and report
+ * the corruption.  Therefore, each scrubber will allocate an empty
+ * transaction, attach buffers to it, and cancel the transaction at the
+ * end of the scrub run.  Cancelling a non-dirty transaction simply
+ * unlocks the buffers.
+ *
+ * There are four pieces of data that scrub can communicate to
Quoted 8 lines

fs/xfs/scrub/scrub.c line 108

Quoted 6 lines
fs/xfs/scrub/scrub.c +6 -0
+ * userspace.  The first is the error code (errno), which can be used to
+ * communicate operational errors in performing the scrub.  There are
+ * also three flags that can be set in the scrub context.  If the data
+ * structure itself is corrupt, the CORRUPT flag will be set.  If
+ * the metadata is correct but otherwise suboptimal, the PREEN flag
+ * will be set.

Did you mean to describe other flags here?

Somewhere; the other flags get added in whichever patch(es) start using them.

Quoted 11 lines

fs/xfs/scrub/scrub.c line 117

Quoted 9 lines
fs/xfs/scrub/scrub.c +9 -0
+ */
+
+/* Scrub setup and teardown */
+
+/* Free all the resources and finish the transactions. */
+STATIC int
+xfs_scrub_teardown(
+	struct xfs_scrub_context	*sc,
+	int				error)

What's the purpose of passing error just to return it?

Eventually repair needs it to decide if it's cancelling the transaction or commiting a repair. I suppose I could remove it here and add it back later.

Quoted 116 lines
Quoted 116 lines
fs/xfs/scrub/scrub.c +100 -1
+{
+	if (sc->tp) {
+		xfs_trans_cancel(sc->tp);
+		sc->tp = NULL;
+	}
+	return error;
+}
+
+/* Scrubbing dispatch. */
+
+static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
+};
+
 /* Dispatch metadata scrubbing. */
 int
 xfs_scrub_metadata(
 	struct xfs_inode		*ip,
 	struct xfs_scrub_metadata	*sm)
 {
-	return -EOPNOTSUPP;
+	struct xfs_scrub_context	sc;
+	struct xfs_mount		*mp = ip->i_mount;
+	const struct xfs_scrub_meta_ops	*ops;
+	bool				try_harder = false;
+	int				error = 0;
+
+	trace_xfs_scrub_start(ip, sm, error);
+
+	/* Forbidden if we are shut down or mounted norecovery. */
+	error = -ESHUTDOWN;
+	if (XFS_FORCED_SHUTDOWN(mp))
+		goto out;
+	error = -ENOTRECOVERABLE;
+	if (mp->m_flags & XFS_MOUNT_NORECOVERY)
+		goto out;
+
+	/* Check our inputs. */
+	error = -EINVAL;
+	sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
+	if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
+		goto out;
+	if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
+		goto out;
+
+	/* Do we know about this type of metadata? */
+	error = -ENOENT;
+	if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
+		goto out;
+	ops = &meta_scrub_ops[sm->sm_type];
+	if (ops->scrub == NULL)
+		goto out;
+
+	/* Does this fs even support this type of metadata? */
+	if (ops->has && !ops->has(&mp->m_sb))
+		goto out;
+
+	/* We don't know how to repair anything yet. */
+	error = -EOPNOTSUPP;
+	if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
+		goto out;
+
+	/* This isn't a stable feature.  Use with care. */
+	{
+		static bool warned;
+
+		if (!warned)
+			xfs_alert(mp,
+	"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
+		warned = true;
+	}
+
+retry_op:
+	/* Set up for the operation. */
+	memset(&sc, 0, sizeof(sc));
+	sc.mp = ip->i_mount;
+	sc.sm = sm;
+	sc.ops = ops;
+	sc.try_harder = try_harder;
+	error = sc.ops->setup(&sc, ip);
+	if (error)
+		goto out_teardown;
+
+	/* Scrub for errors. */
+	error = sc.ops->scrub(&sc);
+	if (!try_harder && error == -EDEADLOCK) {
+		/*
+		 * Scrubbers return -EDEADLOCK to mean 'try harder'.
+		 * Tear down everything we hold, then set up again with
+		 * preparation for worst-case scenarios.
+		 */
+		error = xfs_scrub_teardown(&sc, 0);
+		if (error)
+			goto out;
+		try_harder = true;
+		goto retry_op;
+	} else if (error)
+		goto out_teardown;
+
+	if (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
+			       XFS_SCRUB_OFLAG_XCORRUPT))
+		xfs_alert_ratelimited(mp, "Corruption detected during scrub.");
+
+out_teardown:
+	error = xfs_scrub_teardown(&sc, error);
+out:
+	trace_xfs_scrub_done(ip, sm, error);
+	return error;
 }
fs/xfs/scrub/scrub.h +0 -0
index eb1cd9d..b271b2a 100644
@@ -20,6 +20,25 @@
 #ifndef __XFS_SCRUB_SCRUB_H__
 #define __XFS_SCRUB_SCRUB_H__
 
Quoted 11 lines

fs/xfs/scrub/scrub.h line 29

Quoted 7 lines
fs/xfs/scrub/scrub.h +7 -0
+struct xfs_scrub_context;
+
+struct xfs_scrub_meta_ops {
+	int		(*setup)(struct xfs_scrub_context *,
+				 struct xfs_inode *);
+	int		(*scrub)(struct xfs_scrub_context *);
+	bool		(*has)(struct xfs_sb *);

I assume 'has' is to identify whether a particular mount supports a particular feature. I suppose a better name would be nice here, or perhaps just a comment to outline the purpose of each callout.

I'll add some comments.

--D

Quoted 81 lines

Brian

Quoted 71 lines
fs/xfs/scrub/scrub.h +12 -0
+};
+
+struct xfs_scrub_context {
+	/* General scrub state. */
+	struct xfs_mount		*mp;
+	struct xfs_scrub_metadata	*sm;
+	const struct xfs_scrub_meta_ops	*ops;
+	struct xfs_trans		*tp;
+	struct xfs_inode		*ip;
+	bool				try_harder;
+};
+
 /* Metadata scrubbers */
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.h +43 -1
index a95a7c8..688517e 100644
@@ -25,6 +25,49 @@
 
 #include <linux/tracepoint.h>
 
+DECLARE_EVENT_CLASS(xfs_scrub_class,
+	TP_PROTO(struct xfs_inode *ip, struct xfs_scrub_metadata *sm,
+		 int error),
+	TP_ARGS(ip, sm, error),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(xfs_ino_t, ino)
+		__field(unsigned int, type)
+		__field(xfs_agnumber_t, agno)
+		__field(xfs_ino_t, inum)
+		__field(unsigned int, gen)
+		__field(unsigned int, flags)
+		__field(int, error)
+	),
+	TP_fast_assign(
+		__entry->dev = ip->i_mount->m_super->s_dev;
+		__entry->ino = ip->i_ino;
+		__entry->type = sm->sm_type;
+		__entry->agno = sm->sm_agno;
+		__entry->inum = sm->sm_ino;
+		__entry->gen = sm->sm_gen;
+		__entry->flags = sm->sm_flags;
+		__entry->error = error;
+	),
+	TP_printk("dev %d:%d ino %llu type %u agno %u inum %llu gen %u flags 0x%x error %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->ino,
+		  __entry->type,
+		  __entry->agno,
+		  __entry->inum,
+		  __entry->gen,
+		  __entry->flags,
+		  __entry->error)
+)
+#define DEFINE_SCRUB_EVENT(name) \
+DEFINE_EVENT(xfs_scrub_class, name, \
+	TP_PROTO(struct xfs_inode *ip, struct xfs_scrub_metadata *sm, \
+		 int error), \
+	TP_ARGS(ip, sm, error))
+
+DEFINE_SCRUB_EVENT(xfs_scrub_start);
+DEFINE_SCRUB_EVENT(xfs_scrub_done);
+
 #endif /* _TRACE_XFS_SCRUB_TRACE_H */
 
 #undef TRACE_INCLUDE_PATH
 
--
Quoted 3 lines

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

#14 lore raw

PATCH v10 5/27 xfs: test the scrub ioctl

In reply to Darrick J. Wong

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

Create a test scrubber with id 0. This will be used by xfs_scrub to probe the kernel's abilities to scrub (and repair) the metadata.

---
 fs/xfs/Makefile        |    1 +
 fs/xfs/libxfs/xfs_fs.h |    3 ++
 fs/xfs/scrub/common.c  |   60 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h  |   44 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |   33 ++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h   |    1 +
 fs/xfs/scrub/trace.c   |    1 +
 7 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/common.c
 create mode 100644 fs/xfs/scrub/common.h
fs/xfs/Makefile +1 -0
index f4312bc..ca14595 100644
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
+				   common.o \
 				   scrub.o \
 				   )
 endif
fs/xfs/libxfs/xfs_fs.h +2 -1
index a4b4c8c..5105bad 100644
@@ -483,9 +483,10 @@ struct xfs_scrub_metadata {
  */
 
 /* Scrub subcommands. */
+#define XFS_SCRUB_TYPE_TEST	0	/* presence test ioctl */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	0
+#define XFS_SCRUB_TYPE_NR	1
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.c new file +60 -0

60 lines not on this page · raw message

fs/xfs/scrub/common.h new file +44 -0

44 lines not on this page · raw message

fs/xfs/scrub/scrub.c +33 -0
index 7cf518e..7936a23 100644
@@ -42,6 +42,7 @@
 #include "xfs_rmap_btree.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
+#include "scrub/common.h"
 #include "scrub/trace.h"
 
 /*
@@ -108,6 +109,34 @@
  * will be set.
  */
 
+/*
+ * Test scrubber -- userspace uses this to probe if we're willing to
+ * scrub or repair a given mountpoint.
+ */
+int
+xfs_scrub_tester(
+	struct xfs_scrub_context	*sc)
+{
+	if (sc->sm->sm_ino || sc->sm->sm_agno)
+		return -EINVAL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_CORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_PREEN)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XFAIL)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XCORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_INCOMPLETE)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_INCOMPLETE;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_WARNING)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
+	if (sc->sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
+		return -ENOENT;
+
+	return 0;
+}
+
 /* Scrub setup and teardown */
 
 /* Free all the resources and finish the transactions. */
@@ -126,6 +155,10 @@ xfs_scrub_teardown(
 /* Scrubbing dispatch. */
 
 static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
+	{ /* ioctl presence test */
+		.setup	= xfs_scrub_setup_fs,
+		.scrub	= xfs_scrub_tester,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index b271b2a..2528039 100644
@@ -40,5 +40,6 @@ struct xfs_scrub_context {
 };
 
 /* Metadata scrubbers */
+int xfs_scrub_tester(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c +1 -0
index c59fd41..88b5ccb 100644
@@ -32,6 +32,7 @@
 #include "xfs_trans.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
+#include "scrub/common.h"
 
 /*
  * We include this last to have the helpers above available for the trace
#15 lore raw

Re: [PATCH 05/27] xfs: test the scrub ioctl

Quoted 36 lines · Darrick J. Wong

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

Create a test scrubber with id 0. This will be used by xfs_scrub to probe the kernel's abilities to scrub (and repair) the metadata.

---
 fs/xfs/Makefile        |    1 +
 fs/xfs/libxfs/xfs_fs.h |    3 ++
 fs/xfs/scrub/common.c  |   60 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h  |   44 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |   33 ++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h   |    1 +
 fs/xfs/scrub/trace.c   |    1 +
 7 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/common.c
 create mode 100644 fs/xfs/scrub/common.h
fs/xfs/Makefile +1 -0
index f4312bc..ca14595 100644
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
+				   common.o \
 				   scrub.o \
 				   )
 endif
fs/xfs/libxfs/xfs_fs.h +0 -0
index a4b4c8c..5105bad 100644

fs/xfs/libxfs/xfs_fs.h struct xfs_scrub_metadata { · line 486

Quoted 4 lines
fs/xfs/libxfs/xfs_fs.h +1 -0
@@ -483,9 +483,10 @@ struct xfs_scrub_metadata {
  */
 
 /* Scrub subcommands. */
+#define XFS_SCRUB_TYPE_TEST	0	/* presence test ioctl */

Shouldn't we call this a "probe" - as in "probe for support" so it doesn't get confused with "use this to test whether scrub works"

Quoted 62 lines
fs/xfs/libxfs/xfs_fs.h +1 -1
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	0
+#define XFS_SCRUB_TYPE_NR	1
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.c new file +50 -0
new file mode 100644
index 0000000..13ccb36
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/trace.h"
+
+/* Common code for the metadata scrubbers. */
+
+/* Per-scrubber setup functions */

fs/xfs/scrub/common.c line 60

Quoted 10 lines
fs/xfs/scrub/common.c new file +10 -0
+
+/* Set us up with a transaction and an empty context. */
+int
+xfs_scrub_setup_fs(
+	struct xfs_scrub_context	*sc,
+	struct xfs_inode		*ip)
+{
+	return xfs_scrub_trans_alloc(sc->sm, sc->mp,
+			&M_RES(sc->mp)->tr_itruncate, 0, 0, 0, &sc->tp);
+}

Using the truncate transaction reservation really needs explaining here....

.....

Quoted 5 lines

+/*
+ * Test scrubber -- userspace uses this to probe if we're willing to
+ * scrub or repair a given mountpoint.
+ */

Yup, definitely should be called xfs_scrub_probe()....

Quoted 10 lines
fs/xfs/scrub/common.c new file +10 -0
+int
+xfs_scrub_tester(
+	struct xfs_scrub_context	*sc)
+{
+	if (sc->sm->sm_ino || sc->sm->sm_agno)
+		return -EINVAL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_CORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_PREEN)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;

fs/xfs/scrub/common.c

Quoted 10 lines
fs/xfs/scrub/common.c new file +10 -0
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XFAIL)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XCORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_INCOMPLETE)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_INCOMPLETE;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_WARNING)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
+	if (sc->sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
+		return -ENOENT;

Shouldn't this check should be first? If not, comment to explain?

Also, I find that really hard to parse because it's so dense and so much is repeated over and over again (makes my pattern matching brain cells scream). It's copying the exact same flags from sc->sm->sm_gen to sc->sm->sm_flags, so why not somethign like:

	struct xfs_scrub_m...	*sm = sc->sm;

	if (sm->sm_ino || sm->sm_agno)
		return -EINVAL;

	sm->flags = sm->sm_gen & XFS_SCRUB_FLAGS_OUT;
	if (sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
		return -ENOENT;

Cheers,

Dave.

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

Re: [PATCH 05/27] xfs: test the scrub ioctl

Quoted 39 lines · Dave Chinner
Quoted 38 lines · Darrick J. Wong

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

Create a test scrubber with id 0. This will be used by xfs_scrub to probe the kernel's abilities to scrub (and repair) the metadata.

---
 fs/xfs/Makefile        |    1 +
 fs/xfs/libxfs/xfs_fs.h |    3 ++
 fs/xfs/scrub/common.c  |   60 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h  |   44 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |   33 ++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.h   |    1 +
 fs/xfs/scrub/trace.c   |    1 +
 7 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/common.c
 create mode 100644 fs/xfs/scrub/common.h
fs/xfs/Makefile +1 -0
index f4312bc..ca14595 100644
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
+				   common.o \
 				   scrub.o \
 				   )
 endif
fs/xfs/libxfs/xfs_fs.h +0 -0
index a4b4c8c..5105bad 100644
@@ -483,9 +483,10 @@ struct xfs_scrub_metadata {
  */
 
Quoted 5 lines

fs/xfs/libxfs/xfs_fs.h struct xfs_scrub_metadata { · line 486

Quoted 2 lines
fs/xfs/libxfs/xfs_fs.h +1 -0
 /* Scrub subcommands. */
+#define XFS_SCRUB_TYPE_TEST	0	/* presence test ioctl */

Shouldn't we call this a "probe" - as in "probe for support" so it doesn't get confused with "use this to test whether scrub works"

I'd thought "test" as in "test if it's even there", but "probe" works just as well and (hopefully) confuses everyone less.

Quoted 68 lines
Quoted 68 lines
fs/xfs/libxfs/xfs_fs.h +1 -1
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	0
+#define XFS_SCRUB_TYPE_NR	1
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.c new file +55 -0
new file mode 100644
index 0000000..13ccb36
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/trace.h"
+
+/* Common code for the metadata scrubbers. */
+
+/* Per-scrubber setup functions */
+
+/* Set us up with a transaction and an empty context. */
+int
+xfs_scrub_setup_fs(
+	struct xfs_scrub_context	*sc,
Quoted 8 lines

fs/xfs/scrub/common.c line 60

Quoted 5 lines
fs/xfs/scrub/common.c new file +5 -0
+	struct xfs_inode		*ip)
+{
+	return xfs_scrub_trans_alloc(sc->sm, sc->mp,
+			&M_RES(sc->mp)->tr_itruncate, 0, 0, 0, &sc->tp);
+}

Using the truncate transaction reservation really needs explaining here....

/*
* Reserve a transaction with the largest reservation so that we can
* handle the worst case log item space requirement if we have to repair
* something big.
*/

Hm, come to think of it all callres of xfs_scrub_trans_alloc differ only in the resblks passed in, so I can factor out the rtblks/flags/type parameters.

Quoted 9 lines

.....

Quoted 5 lines

+/*
+ * Test scrubber -- userspace uses this to probe if we're willing to
+ * scrub or repair a given mountpoint.
+ */

Yup, definitely should be called xfs_scrub_probe()....

Ok.

Quoted 29 lines
Quoted 10 lines
fs/xfs/scrub/common.c new file +10 -0
+int
+xfs_scrub_tester(
+	struct xfs_scrub_context	*sc)
+{
+	if (sc->sm->sm_ino || sc->sm->sm_agno)
+		return -EINVAL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_CORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_PREEN)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;

fs/xfs/scrub/common.c

Quoted 10 lines
fs/xfs/scrub/common.c new file +10 -0
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XFAIL)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_XCORRUPT)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_INCOMPLETE)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_INCOMPLETE;
+	if (sc->sm->sm_gen & XFS_SCRUB_OFLAG_WARNING)
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
+	if (sc->sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
+		return -ENOENT;

Shouldn't this check should be first? If not, comment to explain?

Also, I find that really hard to parse because it's so dense and so much is repeated over and over again (makes my pattern matching brain cells scream). It's copying the exact same flags from sc->sm->sm_gen to sc->sm->sm_flags, so why not somethign like:

Quoted 8 lines
	struct xfs_scrub_m...	*sm = sc->sm;

	if (sm->sm_ino || sm->sm_agno)
		return -EINVAL;

	sm->flags = sm->sm_gen & XFS_SCRUB_FLAGS_OUT;
	if (sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
		return -ENOENT;

Yes, that could be:

	if (sm->sm_ino || sm->sm_agno)
		return -EINVAL;
	if (sm->sm_gen & ~XFS_SCRUB_FLAGS_OUT)
		return -ENOENT;

	/* Echo parameters back to userspace to prove that we exist. */
	sm->flags = sm->sm_gen & XFS_SCRUB_FLAGS_OUT;

--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
#17 lore raw

PATCH v10 6/27 xfs: create helpers to record and deal with scrub problems

In reply to Darrick J. Wong

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

Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.

---
 fs/xfs/scrub/common.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   39 ++++++++
 fs/xfs/scrub/trace.h  |  193 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
fs/xfs/scrub/common.c +243 -0
index 13ccb36..cf3f1365 100644
@@ -47,6 +47,249 @@
 
 /* Common code for the metadata scrubbers. */
 
+/* Check for operational errors. */
+bool
+xfs_scrub_op_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	xfs_agblock_t			bno,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_op_error(sc, agno, bno, *error,
+				__return_address);
+		break;
+	}
+	return false;
+}
+
+/* Check for operational errors for a file offset. */
+bool
+xfs_scrub_fblock_op_ok(
+	struct xfs_scrub_context	*sc,
+	int				whichfork,
+	xfs_fileoff_t			offset,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_file_op_error(sc, whichfork, offset, *error,
+				__return_address);
+		break;
+	}
+	return false;
+}
+
+/* Check for metadata block optimization possibilities. */
+bool
+xfs_scrub_block_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_block_preen(sc, agno, bno, __return_address);
+	return fs_ok;
+}
+
+/* Check for inode metadata optimization possibilities. */
+bool
+xfs_scrub_ino_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_inode		*ip = sc->ip;
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	if (bp) {
+		fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+		bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	} else {
+		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+		bno = XFS_INO_TO_AGINO(mp, ip->i_ino);
+	}
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_ino_preen(sc, ip->i_ino, agno, bno, __return_address);
+	return fs_ok;
+}
+
+/* Check for metadata block corruption. */
+bool
+xfs_scrub_block_check_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	trace_xfs_scrub_block_error(sc, agno, bno, __return_address);
+	return fs_ok;
+}
+
+/* Check for inode metadata corruption. */
+bool
+xfs_scrub_ino_check_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_ino_t			ino,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_inode		*ip = sc->ip;
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	if (bp) {
+		fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+		bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	} else {
+		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+		bno = XFS_INO_TO_AGINO(mp, ip->i_ino);
+	}
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	trace_xfs_scrub_ino_error(sc, ino, agno, bno, __return_address);
+	return fs_ok;
+}
+
+/* Check for file fork block corruption. */
+bool
+xfs_scrub_fblock_check_ok(
+	struct xfs_scrub_context	*sc,
+	int				whichfork,
+	xfs_fileoff_t			offset,
+	bool				fs_ok)
+{
+	if (fs_ok)
+		return fs_ok;
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+	trace_xfs_scrub_fblock_error(sc, whichfork, offset, __return_address);
+	return fs_ok;
+}
+
+/* Check for inode metadata non-corruption problems. */
+bool
+xfs_scrub_ino_warn_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_inode		*ip = sc->ip;
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	if (bp) {
+		fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+		agno = XFS_FSB_TO_AGNO(mp, fsbno);
+		bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+	} else {
+		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+		bno = XFS_INO_TO_AGINO(mp, ip->i_ino);
+	}
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
+	trace_xfs_scrub_ino_warning(sc, ip->i_ino, agno, bno, __return_address);
+	return fs_ok;
+}
+
+/* Check for file fork block non-corruption problems. */
+bool
+xfs_scrub_fblock_warn_ok(
+	struct xfs_scrub_context	*sc,
+	int				whichfork,
+	xfs_fileoff_t			offset,
+	bool				fs_ok)
+{
+	if (fs_ok)
+		return fs_ok;
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
+	trace_xfs_scrub_fblock_warning(sc, whichfork, offset, __return_address);
+	return fs_ok;
+}
+
+/* Signal an incomplete scrub. */
+bool
+xfs_scrub_check_thoroughness(
+	struct xfs_scrub_context	*sc,
+	bool				fs_ok)
+{
+	if (fs_ok)
+		return fs_ok;
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_INCOMPLETE;
+	trace_xfs_scrub_incomplete(sc, __return_address);
+	return fs_ok;
+}
+
 /* Per-scrubber setup functions */
 
 /* Set us up with a transaction and an empty context. */
fs/xfs/scrub/common.h +39 -0
index b97df8c..e1bb14b 100644
@@ -38,6 +38,45 @@ xfs_scrub_trans_alloc(
 	return xfs_trans_alloc_empty(mp, tpp);
 }
 
+/* Check for operational errors for a block check. */
+bool xfs_scrub_op_ok(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
+		     xfs_agblock_t bno, int *error);
+
+/* Check for operational errors for a file offset check. */
+bool xfs_scrub_fblock_op_ok(struct xfs_scrub_context *sc, int whichfork,
+			  xfs_fileoff_t offset, int *error);
+
+/* Check for metadata block optimization possibilities. */
+bool xfs_scrub_block_preen_ok(struct xfs_scrub_context *sc, struct xfs_buf *bp,
+			      bool fs_ok);
+
+/* Check for inode metadata optimization possibilities. */
+bool xfs_scrub_ino_preen_ok(struct xfs_scrub_context *sc, struct xfs_buf *bp,
+			    bool fs_ok);
+
+/* Check for metadata block corruption. */
+bool xfs_scrub_block_check_ok(struct xfs_scrub_context *sc, struct xfs_buf *bp,
+			      bool fs_ok);
+
+/* Check for inode metadata corruption. */
+bool xfs_scrub_ino_check_ok(struct xfs_scrub_context *sc, xfs_ino_t ino,
+			    struct xfs_buf *bp, bool fs_ok);
+
+/* Check for file fork block corruption. */
+bool xfs_scrub_fblock_check_ok(struct xfs_scrub_context *sc, int whichfork,
+			       xfs_fileoff_t offset, bool fs_ok);
+
+/* Check for inode metadata non-corruption weirdness problems. */
+bool xfs_scrub_ino_warn_ok(struct xfs_scrub_context *sc, struct xfs_buf *bp,
+			   bool fs_ok);
+
+/* Check for file data block non-corruption weirdness problems. */
+bool xfs_scrub_fblock_warn_ok(struct xfs_scrub_context *sc, int whichfork,
+			      xfs_fileoff_t offset, bool fs_ok);
+
+/* Signal an incomplete scrub. */
+bool xfs_scrub_check_thoroughness(struct xfs_scrub_context *sc, bool fs_ok);
+
 /* Setup functions */
 int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
 
fs/xfs/scrub/trace.h +193 -0

199 lines not on this page · raw message

#18 lore raw

Re: [PATCH 06/27] xfs: create helpers to record and deal with scrub problems

Quoted 39 lines · Darrick J. Wong

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

Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.

---
 fs/xfs/scrub/common.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   39 ++++++++
 fs/xfs/scrub/trace.h  |  193 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
fs/xfs/scrub/common.c +18 -0
index 13ccb36..cf3f1365 100644
@@ -47,6 +47,249 @@
 
 /* Common code for the metadata scrubbers. */
 
+/* Check for operational errors. */
+bool
+xfs_scrub_op_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	xfs_agblock_t			bno,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */

fs/xfs/scrub/common.c line 77

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_op_error(sc, agno, bno, *error,
+				__return_address);
+		break;
+	}
+	return false;
+}

What are the semantics here w.r.t. *error? on some errors it's cleared before we return, on others it's ignored. It's as clear as mud what we should expect from these functions...

Quoted 13 lines
fs/xfs/scrub/common.c +13 -0
+/* Check for metadata block optimization possibilities. */
+bool
+xfs_scrub_block_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)

fs/xfs/scrub/common.c line 130

Quoted 10 lines
fs/xfs/scrub/common.c +10 -0
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_block_preen(sc, agno, bno, __return_address);
+	return fs_ok;
+}

Again, I'm not sure what the return value semantics of the functioon are? Why does the fs_ok return shortcut exist?

Same for all the other functions...

fs/xfs/scrub/common.c line 238

Quoted 7 lines
fs/xfs/scrub/common.c +7 -0
+
+/* Check for inode metadata non-corruption problems. */
+bool
+xfs_scrub_ino_warn_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)

Confusing. What's the difference between a corruption problem and a "non-corruption problem" that requires a warning?

Cheers,

Dave.

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

Re: [PATCH 06/27] xfs: create helpers to record and deal with scrub problems

Quoted 46 lines · Dave Chinner
Quoted 45 lines · Darrick J. Wong

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

Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.

---
 fs/xfs/scrub/common.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   39 ++++++++
 fs/xfs/scrub/trace.h  |  193 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
fs/xfs/scrub/common.c +24 -0
index 13ccb36..cf3f1365 100644
@@ -47,6 +47,249 @@
 
 /* Common code for the metadata scrubbers. */
 
+/* Check for operational errors. */
+bool
+xfs_scrub_op_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	xfs_agblock_t			bno,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_op_error(sc, agno, bno, *error,
+				__return_address);
Quoted 8 lines

fs/xfs/scrub/common.c line 77

Quoted 4 lines
fs/xfs/scrub/common.c +4 -0
+		break;
+	}
+	return false;
+}

What are the semantics here w.r.t. *error? on some errors it's cleared before we return, on others it's ignored. It's as clear as mud what we should expect from these functions...

If there's no error, we return true to tell the caller that it's ok to move on to the next check in its list.

For non-verifier errors (e.g. ENOMEM) we return false to tell the caller that there's no point in it continuing, and we preserve *error so that the caller can return the *error up the stack. Checking stops immediately and the error is handed to userspace.

Verifier errors (EFSBADCRC/EFSCORRUPTED) are recorded in sm_flags and the *error is cleared. We return false to tell the caller that there's point in it continuing with this record. The caller returns zero to its caller, which means that checking continues, having skipped whatever block failed the verifier.

Quoted 18 lines
Quoted 18 lines
fs/xfs/scrub/common.c +18 -0
+/* Check for metadata block optimization possibilities. */
+bool
+xfs_scrub_block_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
Quoted 8 lines

fs/xfs/scrub/common.c line 130

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_block_preen(sc, agno, bno, __return_address);
+	return fs_ok;
+}

Again, I'm not sure what the return value semantics of the functioon are? Why does the fs_ok return shortcut exist?

The fs_ok functions are wrappers around an if test; the results of the if test are passed in as fs_ok.

Therefore, if fs_ok then things are fine and we just skip out.

Otherwise, we found something and we should set sm_flags and jump out.

Quoted 12 lines

Same for all the other functions...

fs/xfs/scrub/common.c line 238

Quoted 7 lines
fs/xfs/scrub/common.c +7 -0
+
+/* Check for inode metadata non-corruption problems. */
+bool
+xfs_scrub_ino_warn_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)

Confusing. What's the difference between a corruption problem and a "non-corruption problem" that requires a warning?

Anything that's less severe than "your fs is corrupt" but otherwise requires administrator review. The inode scrubber sets this for inodes with a -1 uid/gid. XFS seems fine with it, but the VFS treats -1ULL as a magic "doesn't exist" value, and then userspace can't change it. The quota code sets warnings if it detects quota usage exceeding the hard limit, or if the limits are larger than the fs, etc.

In these cases I'd want the administrator to have a look and/or take corrective action, but XFS doesn't flag those situations as fs corruption nor does xfs_repair complain about them as corruption.

--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
#20 lore raw

Re: [PATCH 06/27] xfs: create helpers to record and deal with scrub problems

Darrick,

Excuse the nasty top post, but can you wrap all this up in comments in the code? It all makes sense now you explain it, but I'm not going to remember all that in a couple of months time...

-Dave.

Quoted 142 lines · Darrick J. Wong
Quoted 46 lines · Dave Chinner
Quoted 45 lines · Darrick J. Wong

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

Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.

---
 fs/xfs/scrub/common.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   39 ++++++++
 fs/xfs/scrub/trace.h  |  193 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
fs/xfs/scrub/common.c +24 -0
index 13ccb36..cf3f1365 100644
@@ -47,6 +47,249 @@
 
 /* Common code for the metadata scrubbers. */
 
+/* Check for operational errors. */
+bool
+xfs_scrub_op_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	xfs_agblock_t			bno,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_op_error(sc, agno, bno, *error,
+				__return_address);
Quoted 8 lines

fs/xfs/scrub/common.c line 77

Quoted 4 lines
fs/xfs/scrub/common.c +4 -0
+		break;
+	}
+	return false;
+}

What are the semantics here w.r.t. *error? on some errors it's cleared before we return, on others it's ignored. It's as clear as mud what we should expect from these functions...

If there's no error, we return true to tell the caller that it's ok to move on to the next check in its list.

For non-verifier errors (e.g. ENOMEM) we return false to tell the caller that there's no point in it continuing, and we preserve *error so that the caller can return the *error up the stack. Checking stops immediately and the error is handed to userspace.

Verifier errors (EFSBADCRC/EFSCORRUPTED) are recorded in sm_flags and the *error is cleared. We return false to tell the caller that there's point in it continuing with this record. The caller returns zero to its caller, which means that checking continues, having skipped whatever block failed the verifier.

Quoted 18 lines
Quoted 18 lines
fs/xfs/scrub/common.c +18 -0
+/* Check for metadata block optimization possibilities. */
+bool
+xfs_scrub_block_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
Quoted 8 lines

fs/xfs/scrub/common.c line 130

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_block_preen(sc, agno, bno, __return_address);
+	return fs_ok;
+}

Again, I'm not sure what the return value semantics of the functioon are? Why does the fs_ok return shortcut exist?

The fs_ok functions are wrappers around an if test; the results of the if test are passed in as fs_ok.

Therefore, if fs_ok then things are fine and we just skip out.

Otherwise, we found something and we should set sm_flags and jump out.

Quoted 12 lines

Same for all the other functions...

fs/xfs/scrub/common.c line 238

Quoted 7 lines
fs/xfs/scrub/common.c +7 -0
+
+/* Check for inode metadata non-corruption problems. */
+bool
+xfs_scrub_ino_warn_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)

Confusing. What's the difference between a corruption problem and a "non-corruption problem" that requires a warning?

Anything that's less severe than "your fs is corrupt" but otherwise requires administrator review. The inode scrubber sets this for inodes with a -1 uid/gid. XFS seems fine with it, but the VFS treats -1ULL as a magic "doesn't exist" value, and then userspace can't change it. The quota code sets warnings if it detects quota usage exceeding the hard limit, or if the limits are larger than the fs, etc.

In these cases I'd want the administrator to have a look and/or take corrective action, but XFS doesn't flag those situations as fs corruption nor does xfs_repair complain about them as corruption.

--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
Signature · 2 lines
Dave Chinner
[email protected]
#21 lore raw

Re: [PATCH 06/27] xfs: create helpers to record and deal with scrub problems

Quoted 5 lines · Dave Chinner

Darrick,

Excuse the nasty top post, but can you wrap all this up in comments in the code? It all makes sense now you explain it, but I'm not going to remember all that in a couple of months time...

Already done.

(FWIW top-posting doesn't bother me...)

--D

Quoted 154 lines

-Dave.

Quoted 142 lines · Darrick J. Wong
Quoted 46 lines · Dave Chinner
Quoted 45 lines · Darrick J. Wong

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

Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.

---
 fs/xfs/scrub/common.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   39 ++++++++
 fs/xfs/scrub/trace.h  |  193 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
fs/xfs/scrub/common.c +24 -0
index 13ccb36..cf3f1365 100644
@@ -47,6 +47,249 @@
 
 /* Common code for the metadata scrubbers. */
 
+/* Check for operational errors. */
+bool
+xfs_scrub_op_ok(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	xfs_agblock_t			bno,
+	int				*error)
+{
+	switch (*error) {
+	case 0:
+		return true;
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*error = 0;
+		/* fall through */
+	default:
+		trace_xfs_scrub_op_error(sc, agno, bno, *error,
+				__return_address);
Quoted 8 lines

fs/xfs/scrub/common.c line 77

Quoted 4 lines
fs/xfs/scrub/common.c +4 -0
+		break;
+	}
+	return false;
+}

What are the semantics here w.r.t. *error? on some errors it's cleared before we return, on others it's ignored. It's as clear as mud what we should expect from these functions...

If there's no error, we return true to tell the caller that it's ok to move on to the next check in its list.

For non-verifier errors (e.g. ENOMEM) we return false to tell the caller that there's no point in it continuing, and we preserve *error so that the caller can return the *error up the stack. Checking stops immediately and the error is handed to userspace.

Verifier errors (EFSBADCRC/EFSCORRUPTED) are recorded in sm_flags and the *error is cleared. We return false to tell the caller that there's point in it continuing with this record. The caller returns zero to its caller, which means that checking continues, having skipped whatever block failed the verifier.

Quoted 18 lines
Quoted 18 lines
fs/xfs/scrub/common.c +18 -0
+/* Check for metadata block optimization possibilities. */
+bool
+xfs_scrub_block_preen_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fsblock_t			fsbno;
+	xfs_agnumber_t			agno;
+	xfs_agblock_t			bno;
+
+	if (fs_ok)
+		return fs_ok;
+
+	fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+	agno = XFS_FSB_TO_AGNO(mp, fsbno);
+	bno = XFS_FSB_TO_AGBNO(mp, fsbno);
Quoted 8 lines

fs/xfs/scrub/common.c line 130

Quoted 5 lines
fs/xfs/scrub/common.c +5 -0
+
+	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
+	trace_xfs_scrub_block_preen(sc, agno, bno, __return_address);
+	return fs_ok;
+}

Again, I'm not sure what the return value semantics of the functioon are? Why does the fs_ok return shortcut exist?

The fs_ok functions are wrappers around an if test; the results of the if test are passed in as fs_ok.

Therefore, if fs_ok then things are fine and we just skip out.

Otherwise, we found something and we should set sm_flags and jump out.

Quoted 12 lines

Same for all the other functions...

fs/xfs/scrub/common.c line 238

Quoted 7 lines
fs/xfs/scrub/common.c +7 -0
+
+/* Check for inode metadata non-corruption problems. */
+bool
+xfs_scrub_ino_warn_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_buf			*bp,
+	bool				fs_ok)

Confusing. What's the difference between a corruption problem and a "non-corruption problem" that requires a warning?

Anything that's less severe than "your fs is corrupt" but otherwise requires administrator review. The inode scrubber sets this for inodes with a -1 uid/gid. XFS seems fine with it, but the VFS treats -1ULL as a magic "doesn't exist" value, and then userspace can't change it. The quota code sets warnings if it detects quota usage exceeding the hard limit, or if the limits are larger than the fs, etc.

In these cases I'd want the administrator to have a look and/or take corrective action, but XFS doesn't flag those situations as fs corruption nor does xfs_repair complain about them as corruption.

--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
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
#22 lore raw

PATCH v10 7/27 xfs: create helpers to scrub a metadata btree

In reply to Darrick J. Wong

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

Create helper functions and tracepoints to deal with errors while scrubbing a metadata btree.

---
 fs/xfs/Makefile      |    1 
 fs/xfs/scrub/btree.c |  114 +++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/btree.h |   57 ++++++++++++++++++
 fs/xfs/scrub/trace.c |   14 ++++
 fs/xfs/scrub/trace.h |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 348 insertions(+)
 create mode 100644 fs/xfs/scrub/btree.c
 create mode 100644 fs/xfs/scrub/btree.h
fs/xfs/Makefile +1 -0
index ca14595..5888b9f 100644
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
+				   btree.o \
 				   common.o \
 				   scrub.o \
 				   )
fs/xfs/scrub/btree.c new file +114 -0

114 lines not on this page · raw message

fs/xfs/scrub/btree.h new file +57 -0

57 lines not on this page · raw message

fs/xfs/scrub/trace.c +14 -0
index 88b5ccb..ee00115 100644
@@ -30,10 +30,24 @@
 #include "xfs_inode.h"
 #include "xfs_btree.h"
 #include "xfs_trans.h"
+#include "xfs_bit.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 
+/* Figure out which block the btree cursor was pointing to. */
+static inline xfs_fsblock_t
+xfs_scrub_btree_cur_fsbno(
+	struct xfs_btree_cur		*cur,
+	int				level)
+{
+	if (level < cur->bc_nlevels && cur->bc_bufs[level])
+		return XFS_DADDR_TO_FSB(cur->bc_mp, cur->bc_bufs[level]->b_bn);
+	else if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+		return XFS_INO_TO_FSB(cur->bc_mp, cur->bc_private.b.ip->i_ino);
+	return XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno, 0);
+}
+
 /*
  * We include this last to have the helpers above available for the trace
  * event implementations.
fs/xfs/scrub/trace.h +162 -0

168 lines not on this page · raw message

#23 lore raw

Re: [PATCH 07/27] xfs: create helpers to scrub a metadata btree

Quoted 19 lines · Darrick J. Wong
fs/xfs/scrub/btree.c new file +19 -0
+/* Check for btree operation errors . */
+bool
+xfs_scrub_btree_op_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_btree_cur		*cur,
+	int				level,
+	int				*error)
+{
+	if (*error == 0)
+		return true;
+
+	switch (*error) {
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */

fs/xfs/scrub/btree.c line 69

Quoted 10 lines
fs/xfs/scrub/btree.c new file +10 -0
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*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);
+		else
+			trace_xfs_scrub_btree_op_error(sc, cur, level,
+					*error, __return_address);

Why different tracepoints when you could just output the cur->bc_flags in the trace output and use the same tracepoint? Doing that looks like it would simplify the tracepoint code in this patch....

fs/xfs/scrub/trace.c line 46

Quoted 9 lines
fs/xfs/scrub/trace.c +9 -0
+/* Figure out which block the btree cursor was pointing to. */
+static inline xfs_fsblock_t
+xfs_scrub_btree_cur_fsbno(
+	struct xfs_btree_cur		*cur,
+	int				level)
+{
+	if (level < cur->bc_nlevels && cur->bc_bufs[level])
+		return XFS_DADDR_TO_FSB(cur->bc_mp, cur->bc_bufs[level]->b_bn);
+	else if (cur->bc_flags & XFS_BTREE_LONG_PTRS)

no need for "else if", just "if" will do.

fs/xfs/scrub/trace.c line 47

Quoted 1 line
fs/xfs/scrub/trace.c +1 -0
+		return XFS_INO_TO_FSB(cur->bc_mp, cur->bc_private.b.ip->i_ino);

This makes no sense to me. Why are we returning the block address of the inode here? It's not part of the btree....

fs/xfs/scrub/trace.c line 48

Quoted 1 line
fs/xfs/scrub/trace.c +1 -0
+	return XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno, 0);

Nor is the first block of the AG....

Cheers,

Dave.

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

Re: [PATCH 07/27] xfs: create helpers to scrub a metadata btree

Quoted 27 lines · Dave Chinner
Quoted 26 lines · Darrick J. Wong
fs/xfs/scrub/btree.c new file +26 -0
+/* Check for btree operation errors . */
+bool
+xfs_scrub_btree_op_ok(
+	struct xfs_scrub_context	*sc,
+	struct xfs_btree_cur		*cur,
+	int				level,
+	int				*error)
+{
+	if (*error == 0)
+		return true;
+
+	switch (*error) {
+	case -EDEADLOCK:
+		/* Used to restart an op with deadlock avoidance. */
+		trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+		break;
+	case -EFSBADCRC:
+	case -EFSCORRUPTED:
+		/* Note the badness but don't abort. */
+		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+		*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);
Quoted 8 lines

fs/xfs/scrub/btree.c line 69

Quoted 3 lines
fs/xfs/scrub/btree.c new file +3 -0
+		else
+			trace_xfs_scrub_btree_op_error(sc, cur, level,
+					*error, __return_address);

Why different tracepoints when you could just output the cur->bc_flags in the trace output and use the same tracepoint? Doing that looks like it would simplify the tracepoint code in this patch....

The ifork_btree_op_error tracepoint records the inode number and whichfork, whereas the btree_op_error of course does not. I don't know how to write a tracepoint that changes the TP_printk format string & arguments based on the inputs. If the bmap scrubber sees a problem with an inode's fork, I want to know which inode and which fork without having to go find the previous xfs_scrub_start trace.

Quoted 19 lines

fs/xfs/scrub/trace.c line 46

Quoted 10 lines
fs/xfs/scrub/trace.c +9 -0
+/* Figure out which block the btree cursor was pointing to. */
+static inline xfs_fsblock_t
+xfs_scrub_btree_cur_fsbno(
+	struct xfs_btree_cur		*cur,
+	int				level)
+{
+	if (level < cur->bc_nlevels && cur->bc_bufs[level])
+		return XFS_DADDR_TO_FSB(cur->bc_mp, cur->bc_bufs[level]->b_bn);
+	else if (cur->bc_flags & XFS_BTREE_LONG_PTRS)

no need for "else if", just "if" will do.

fs/xfs/scrub/trace.c line 47

Quoted 1 line
fs/xfs/scrub/trace.c +1 -0
+		return XFS_INO_TO_FSB(cur->bc_mp, cur->bc_private.b.ip->i_ino);

This makes no sense to me. Why are we returning the block address of the inode here? It's not part of the btree....

Quoted 3 lines

fs/xfs/scrub/trace.c line 48

Quoted 1 line
fs/xfs/scrub/trace.c +1 -0
+	return XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno, 0);

Nor is the first block of the AG....

I should've written into the comment that the fsb returned by this function is used only for tracepoint output.

These two clauses are fallbacks to handle the case that this level of the btree cursor doesn't point to a buffer. If there is no xfs_buf and this is a long-pointer btree then either we're looking at the btree root in the inode or staring into space, for either of these we might as well report the block that contains the inode. If there is no xfs_buf and it's a short-pointer btree then it must be an AG tree and so report the AG.

I could be persuaded that if we don't have a buffer and it's not the inode root then just report NULLFSBLOCK in the tracepoint, since I revised all the tracepoints to contain the inum/agno anyway.

--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
#25 lore raw

PATCH v10 8/27 xfs: scrub the shape of a metadata btree

In reply to Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)
fs/xfs/libxfs/xfs_btree.c +14 -2
index 5bfb882..c4d8b47 100644
@@ -1027,7 +1027,7 @@ xfs_btree_setbuf(
 	}
 }
 
-STATIC int
+bool
 xfs_btree_ptr_is_null(
 	struct xfs_btree_cur	*cur,
 	union xfs_btree_ptr	*ptr)
@@ -1052,7 +1052,7 @@ xfs_btree_set_ptr_null(
 /*
  * Get/set/init sibling pointers
  */
-STATIC void
+void
 xfs_btree_get_sibling(
 	struct xfs_btree_cur	*cur,
 	struct xfs_btree_block	*block,
@@ -4914,3 +4914,15 @@ xfs_btree_count_blocks(
 	return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper,
 			blocks);
 }
+
+/* Compare two btree pointers. */
+int64_t
+xfs_btree_diff_two_ptrs(
+	struct xfs_btree_cur		*cur,
+	const union xfs_btree_ptr	*a,
+	const union xfs_btree_ptr	*b)
+{
+	if (cur->bc_flags & XFS_BTREE_LONG_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);
+}
fs/xfs/libxfs/xfs_btree.h +7 -0
index f2a88c3..0daf524 100644
@@ -517,5 +517,12 @@ int xfs_btree_lookup_get_block(struct xfs_btree_cur *cur, int level,
 		union xfs_btree_ptr *pp, struct xfs_btree_block **blkp);
 struct xfs_btree_block *xfs_btree_get_block(struct xfs_btree_cur *cur,
 		int level, struct xfs_buf **bpp);
+bool xfs_btree_ptr_is_null(struct xfs_btree_cur *cur, union xfs_btree_ptr *ptr);
+int64_t xfs_btree_diff_two_ptrs(struct xfs_btree_cur *cur,
+				const union xfs_btree_ptr *a,
+				const union xfs_btree_ptr *b);
+void xfs_btree_get_sibling(struct xfs_btree_cur *cur,
+			   struct xfs_btree_block *block,
+			   union xfs_btree_ptr *ptr, int lr);
 
 #endif	/* __XFS_BTREE_H__ */
fs/xfs/scrub/btree.c +234 -2

246 lines not on this page · raw message

fs/xfs/scrub/common.h +13 -0
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
#26 lore raw

Re: [PATCH 08/27] xfs: scrub the shape of a metadata btree

Quoted 18 lines · Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)

...

Quoted 4 lines
fs/xfs/scrub/btree.c +0 -0
index adf5d09..a9c2bf3 100644

...

Quoted 29 lines
fs/xfs/scrub/btree.c +23 -2
@@ -109,6 +255,92 @@ xfs_scrub_btree(
 	struct xfs_owner_info		*oinfo,
 	void				*private)
 {
-	xfs_scrub_btree_op_ok(sc, cur, 0, false);
-	return -EOPNOTSUPP;
+	struct xfs_scrub_btree		bs = {0};
+	union xfs_btree_ptr		ptr;
+	union xfs_btree_ptr		*pp;
+	struct xfs_btree_block		*block;
+	int				level;
+	struct xfs_buf			*bp;
+	int				i;
+	int				error = 0;
+
+	/* Initialize scrub state */
+	bs.cur = cur;
+	bs.scrub_rec = scrub_fn;
+	bs.oinfo = oinfo;
+	bs.firstrec = true;
+	bs.private = private;
+	bs.sc = sc;
+	for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+		bs.firstkey[i] = true;
+	INIT_LIST_HEAD(&bs.to_check);
+
+	/* Don't try to check a tree with a height we can't handle. */
+	if (!xfs_scrub_btree_check_ok(sc, cur, 0, cur->bc_nlevels > 0 &&
+			cur->bc_nlevels <= XFS_BTREE_MAXLEVELS))

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

Quoted 10 lines
fs/xfs/scrub/btree.c +10 -0
+		goto out;
+
+	/* Make sure the root isn't in the superblock. */
+	if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)) {
+		cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+		error = xfs_scrub_btree_ptr(&bs, cur->bc_nlevels, &ptr);
+		if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+			goto out;
+	}
+

This is kind of in line with Dave's comments on the previous patch that introduce some of these helpers. I just glanced over them for now because I didn't have enough context to grok the error processing.

FWIW, the btree_op_ok()/btree_check_ok() stuff kind of makes my eyes cross a bit because I can't easily see the logic checks or distinguish between those and error code checks. This is also a bit confusing because it looks like we overload return codes for various things. E.g., we generate -EFSCORRUPTED in some cases just so the caller can set the state on the context and clear it, but then we still use the fact that an error _was_ set to control the flow of the task via the op_ok() return value. This makes some of the code flow/decision making logic hard to follow, particularly since some of that state looks like it can be lost.

Case in point.. what happens if say xfs_btree_increment() returns -EFSCORRUPTED back to xfs_scrub_btree_block_check_sibling()? It looks to me that the latter calls btree_op_ok() to set the corrupt state, clears the error code and skips out appropriately. xfs_scrub_btree_block_check_sibling() now returns zero, which potentially bubbles up to xfs_scrub_btree() where we check the error code again. Is it expected that error == 0 here? What is supposed to happen here?

I'm wondering if this could all be made more clear by trying to explicitly separate out operational errors, scrub failures and whatever we want to call the logic that clears an -EFSCORRUPTED/-EFSBADCRC error code but still indicates something happened. :P

For starters, rather than wrap every logic check with btree_op_check(), could we use explicit logic and let each function update the context based on problems it found? For example, something like the following is much more easy to read for me than the associated logic above:

	/* Don't try to check a tree with a height we can't handle. */
	if (!(cur->bc_nlevels > 0 &&
	      cur->bc_nlevels <= XFS_BTREE_MAXLEVELS)) {
		xfs_scrub_sc_corrupt(...);
		goto out;
	}

And of course the context update calls could be factored into an out_corrupt label or something where appropriate.

Beyond that, where we need to identify a bit of metadata is busted to perhaps do something like skip it but not abort (as we may have filtered out an -EFSCORRUPTED) return code, could we pass a flag down a particular callchain (i.e., think 'bool *bad' or 'int *stat' a la the core btree code)? Then we can still transfer that state back up the chain and the caller(s) can distinguish operational errors from "this thing is corrupted, act accordingly," regardless of how the corruption was detected.

Brian

Quoted 85 lines
fs/xfs/scrub/btree.c +55 -0
+	/* Load the root of the btree. */
+	level = cur->bc_nlevels - 1;
+	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+	error = xfs_scrub_btree_block(&bs, level, &ptr, &block, &bp);
+	if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+		goto out;
+
+	cur->bc_ptrs[level] = 1;
+
+	while (level < cur->bc_nlevels) {
+		block = xfs_btree_get_block(cur, level, &bp);
+
+		if (level == 0) {
+			/* End of leaf, pop back towards the root. */
+			if (cur->bc_ptrs[level] >
+			    be16_to_cpu(block->bb_numrecs)) {
+				if (level < cur->bc_nlevels - 1)
+					cur->bc_ptrs[level + 1]++;
+				level++;
+				continue;
+			}
+
+			if (xfs_scrub_should_terminate(&error))
+				break;
+
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+
+		/* End of node, pop back towards the root. */
+		if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+			if (level < cur->bc_nlevels - 1)
+				cur->bc_ptrs[level + 1]++;
+			level++;
+			continue;
+		}
+
+		/* Drill another level deeper. */
+		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+		error = xfs_scrub_btree_ptr(&bs, level, pp);
+		if (error) {
+			error = 0;
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+		level--;
+		error = xfs_scrub_btree_block(&bs, level, pp, &block, &bp);
+		if (!xfs_scrub_btree_op_ok(sc, cur, level, &error))
+			goto out;
+
+		cur->bc_ptrs[level] = 1;
+	}
+
+out:
+	return error;
 }
fs/xfs/scrub/common.h +13 -1
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
 
--

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

#27 lore raw

Re: [PATCH 08/27] xfs: scrub the shape of a metadata btree

Quoted 60 lines · Brian Foster
Quoted 18 lines · Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)

...

Quoted 4 lines
fs/xfs/scrub/btree.c +0 -0
index adf5d09..a9c2bf3 100644

...

Quoted 35 lines
fs/xfs/scrub/btree.c +29 -2
@@ -109,6 +255,92 @@ xfs_scrub_btree(
 	struct xfs_owner_info		*oinfo,
 	void				*private)
 {
-	xfs_scrub_btree_op_ok(sc, cur, 0, false);
-	return -EOPNOTSUPP;
+	struct xfs_scrub_btree		bs = {0};
+	union xfs_btree_ptr		ptr;
+	union xfs_btree_ptr		*pp;
+	struct xfs_btree_block		*block;
+	int				level;
+	struct xfs_buf			*bp;
+	int				i;
+	int				error = 0;
+
+	/* Initialize scrub state */
+	bs.cur = cur;
+	bs.scrub_rec = scrub_fn;
+	bs.oinfo = oinfo;
+	bs.firstrec = true;
+	bs.private = private;
+	bs.sc = sc;
+	for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+		bs.firstkey[i] = true;
+	INIT_LIST_HEAD(&bs.to_check);
+
+	/* Don't try to check a tree with a height we can't handle. */
+	if (!xfs_scrub_btree_check_ok(sc, cur, 0, cur->bc_nlevels > 0 &&
+			cur->bc_nlevels <= XFS_BTREE_MAXLEVELS))
+		goto out;
+
+	/* Make sure the root isn't in the superblock. */
+	if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)) {
+		cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+		error = xfs_scrub_btree_ptr(&bs, cur->bc_nlevels, &ptr);
Quoted 8 lines

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

Quoted 4 lines
fs/xfs/scrub/btree.c +4 -0
+		if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+			goto out;
+	}
+

This is kind of in line with Dave's comments on the previous patch that introduce some of these helpers. I just glanced over them for now because I didn't have enough context to grok the error processing.

(That's been a struggle with this patchset -- some of these helpers don't get used until much further in the patchset. I could have sprinkled them into whichever patch uses them first, but now the hunks are all over the series, I'd have to do more dependency tracking to make sure bisect continues to work, and the frequency of auto-merge failures as I push and pop the stack increase dramatically.)

Quoted 11 lines

FWIW, the btree_op_ok()/btree_check_ok() stuff kind of makes my eyes cross a bit because I can't easily see the logic checks or distinguish between those and error code checks. This is also a bit confusing because it looks like we overload return codes for various things. E.g., we generate -EFSCORRUPTED in some cases just so the caller can set the state on the context and clear it, but then we still use the fact that an error _was_ set to control the flow of the task via the op_ok() return value. This makes some of the code flow/decision making logic hard to follow, particularly since some of that state looks like it can be lost.

Quoted 8 lines

Case in point.. what happens if say xfs_btree_increment() returns -EFSCORRUPTED back to xfs_scrub_btree_block_check_sibling()? It looks to me that the latter calls btree_op_ok() to set the corrupt state, clears the error code and skips out appropriately. xfs_scrub_btree_block_check_sibling() now returns zero, which potentially bubbles up to xfs_scrub_btree() where we check the error code again. Is it expected that error == 0 here? What is supposed to happen here?

Yes, error == 0 is intended here. Given a block B, we want to check that B->rightsib->leftsib (if the sibling exists at all) point back to B. If the btree_increment operation returns EFSCORRUPTED we don't know if that's because the B->rightsib points at something it's not supposed to, or if B->rightsib points at a btree block, but that sibling block is corrupt. Therefore we set the corrupt flag and bubble error == 0 up the
call stack so that we can check the other records in the btree. This
enables those following with ftrace to see everything that scrub thinks is wrong with that piece of metadata.

IOWs, we only use error code returns for "runtime error, abort the whole operation immediately".

Quoted 17 lines

I'm wondering if this could all be made more clear by trying to explicitly separate out operational errors, scrub failures and whatever we want to call the logic that clears an -EFSCORRUPTED/-EFSBADCRC error code but still indicates something happened. :P

For starters, rather than wrap every logic check with btree_op_check(), could we use explicit logic and let each function update the context based on problems it found? For example, something like the following is much more easy to read for me than the associated logic above:

	/* Don't try to check a tree with a height we can't handle. */
	if (!(cur->bc_nlevels > 0 &&
	      cur->bc_nlevels <= XFS_BTREE_MAXLEVELS)) {
		xfs_scrub_sc_corrupt(...);
		goto out;
	}
Quoted 2 lines

And of course the context update calls could be factored into an out_corrupt label or something where appropriate.

Yes, that could be done.

Quoted 8 lines

Beyond that, where we need to identify a bit of metadata is busted to perhaps do something like skip it but not abort (as we may have filtered out an -EFSCORRUPTED) return code, could we pass a flag down a particular callchain (i.e., think 'bool *bad' or 'int *stat' a la the core btree code)? Then we can still transfer that state back up the chain and the caller(s) can distinguish operational errors from "this thing is corrupted, act accordingly," regardless of how the corruption was detected.

So far I haven't needed to distinguish between "no problems encountered" and "this callchain hit a verifier error so we just set _CORRUPT" -- scrub always keeps going until it runs out of things to check.

(Maybe I'm missing something?)

--D

Quoted 92 lines

Brian

Quoted 82 lines
fs/xfs/scrub/btree.c +55 -0
+	/* Load the root of the btree. */
+	level = cur->bc_nlevels - 1;
+	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+	error = xfs_scrub_btree_block(&bs, level, &ptr, &block, &bp);
+	if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+		goto out;
+
+	cur->bc_ptrs[level] = 1;
+
+	while (level < cur->bc_nlevels) {
+		block = xfs_btree_get_block(cur, level, &bp);
+
+		if (level == 0) {
+			/* End of leaf, pop back towards the root. */
+			if (cur->bc_ptrs[level] >
+			    be16_to_cpu(block->bb_numrecs)) {
+				if (level < cur->bc_nlevels - 1)
+					cur->bc_ptrs[level + 1]++;
+				level++;
+				continue;
+			}
+
+			if (xfs_scrub_should_terminate(&error))
+				break;
+
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+
+		/* End of node, pop back towards the root. */
+		if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+			if (level < cur->bc_nlevels - 1)
+				cur->bc_ptrs[level + 1]++;
+			level++;
+			continue;
+		}
+
+		/* Drill another level deeper. */
+		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+		error = xfs_scrub_btree_ptr(&bs, level, pp);
+		if (error) {
+			error = 0;
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+		level--;
+		error = xfs_scrub_btree_block(&bs, level, pp, &block, &bp);
+		if (!xfs_scrub_btree_op_ok(sc, cur, level, &error))
+			goto out;
+
+		cur->bc_ptrs[level] = 1;
+	}
+
+out:
+	return error;
 }
fs/xfs/scrub/common.h +13 -1
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
 
--
Quoted 3 lines

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

#28 lore raw

Re: [PATCH 08/27] xfs: scrub the shape of a metadata btree

Quoted 107 lines · Darrick J. Wong
Quoted 60 lines · Brian Foster
Quoted 18 lines · Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)

...

Quoted 4 lines
fs/xfs/scrub/btree.c +0 -0
index adf5d09..a9c2bf3 100644

...

Quoted 35 lines
fs/xfs/scrub/btree.c +29 -2
@@ -109,6 +255,92 @@ xfs_scrub_btree(
 	struct xfs_owner_info		*oinfo,
 	void				*private)
 {
-	xfs_scrub_btree_op_ok(sc, cur, 0, false);
-	return -EOPNOTSUPP;
+	struct xfs_scrub_btree		bs = {0};
+	union xfs_btree_ptr		ptr;
+	union xfs_btree_ptr		*pp;
+	struct xfs_btree_block		*block;
+	int				level;
+	struct xfs_buf			*bp;
+	int				i;
+	int				error = 0;
+
+	/* Initialize scrub state */
+	bs.cur = cur;
+	bs.scrub_rec = scrub_fn;
+	bs.oinfo = oinfo;
+	bs.firstrec = true;
+	bs.private = private;
+	bs.sc = sc;
+	for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+		bs.firstkey[i] = true;
+	INIT_LIST_HEAD(&bs.to_check);
+
+	/* Don't try to check a tree with a height we can't handle. */
+	if (!xfs_scrub_btree_check_ok(sc, cur, 0, cur->bc_nlevels > 0 &&
+			cur->bc_nlevels <= XFS_BTREE_MAXLEVELS))
+		goto out;
+
+	/* Make sure the root isn't in the superblock. */
+	if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)) {
+		cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+		error = xfs_scrub_btree_ptr(&bs, cur->bc_nlevels, &ptr);
Quoted 8 lines

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

Quoted 4 lines
fs/xfs/scrub/btree.c +4 -0
+		if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+			goto out;
+	}
+

This is kind of in line with Dave's comments on the previous patch that introduce some of these helpers. I just glanced over them for now because I didn't have enough context to grok the error processing.

(That's been a struggle with this patchset -- some of these helpers don't get used until much further in the patchset. I could have sprinkled them into whichever patch uses them first, but now the hunks are all over the series, I'd have to do more dependency tracking to make sure bisect continues to work, and the frequency of auto-merge failures as I push and pop the stack increase dramatically.)

Quoted 11 lines

FWIW, the btree_op_ok()/btree_check_ok() stuff kind of makes my eyes cross a bit because I can't easily see the logic checks or distinguish between those and error code checks. This is also a bit confusing because it looks like we overload return codes for various things. E.g., we generate -EFSCORRUPTED in some cases just so the caller can set the state on the context and clear it, but then we still use the fact that an error _was_ set to control the flow of the task via the op_ok() return value. This makes some of the code flow/decision making logic hard to follow, particularly since some of that state looks like it can be lost.

Quoted 8 lines

Case in point.. what happens if say xfs_btree_increment() returns -EFSCORRUPTED back to xfs_scrub_btree_block_check_sibling()? It looks to me that the latter calls btree_op_ok() to set the corrupt state, clears the error code and skips out appropriately. xfs_scrub_btree_block_check_sibling() now returns zero, which potentially bubbles up to xfs_scrub_btree() where we check the error code again. Is it expected that error == 0 here? What is supposed to happen here?

Yes, error == 0 is intended here. Given a block B, we want to check that B->rightsib->leftsib (if the sibling exists at all) point back to B. If the btree_increment operation returns EFSCORRUPTED we don't know if that's because the B->rightsib points at something it's not supposed to, or if B->rightsib points at a btree block, but that sibling block is corrupt. Therefore we set the corrupt flag and bubble error == 0 up the
call stack so that we can check the other records in the btree. This
enables those following with ftrace to see everything that scrub thinks is wrong with that piece of metadata.

Quoted 3 lines

IOWs, we only use error code returns for "runtime error, abort the whole operation immediately".

Ok, so we intentionally have to consume the error here because it doesn't necessarily reflect the corrupted state of the scrubbed block. So IIUC, overloading return codes as such means error handling must either return -EFSCORRUPTED for the current object being corrupted, return some other error for an error in the infrastructure, or clear any -EFSCORRUPTED error generated by checks that don't necessarily mean the current object is corrupted (or too much so to interrupt processing).

Quoted 31 lines
Quoted 17 lines

I'm wondering if this could all be made more clear by trying to explicitly separate out operational errors, scrub failures and whatever we want to call the logic that clears an -EFSCORRUPTED/-EFSBADCRC error code but still indicates something happened. :P

For starters, rather than wrap every logic check with btree_op_check(), could we use explicit logic and let each function update the context based on problems it found? For example, something like the following is much more easy to read for me than the associated logic above:

	/* Don't try to check a tree with a height we can't handle. */
	if (!(cur->bc_nlevels > 0 &&
	      cur->bc_nlevels <= XFS_BTREE_MAXLEVELS)) {
		xfs_scrub_sc_corrupt(...);
		goto out;
	}
Quoted 2 lines

And of course the context update calls could be factored into an out_corrupt label or something where appropriate.

Yes, that could be done.

Quoted 8 lines

Beyond that, where we need to identify a bit of metadata is busted to perhaps do something like skip it but not abort (as we may have filtered out an -EFSCORRUPTED) return code, could we pass a flag down a particular callchain (i.e., think 'bool *bad' or 'int *stat' a la the core btree code)? Then we can still transfer that state back up the chain and the caller(s) can distinguish operational errors from "this thing is corrupted, act accordingly," regardless of how the corruption was detected.

Quoted 4 lines

So far I haven't needed to distinguish between "no problems encountered" and "this callchain hit a verifier error so we just set _CORRUPT" -- scrub always keeps going until it runs out of things to check.

Ok. This is partly speculation on the above (trying to wrap my head around the error consumption bits as is) and partly to try and see if we can make the flow more readable.

In my mind, this is more clear if return codes are reserved for operational/infrastructure errors and the corrupted state of a piece of metadata is its own state. Using the example above, any -EFSCORRUPTED errors from external calls (xfs_btree_check_block(), xfs_btree_increment(), etc.) would always be cleared and replaced with a return 0. The difference between those is the former (check_block()) error sets a 'bad = true' state on the currently scrubbed bit of metadata and the latter (check_sibling()) does not. The latter can of course still set the global corrupted state on the context to track that there is an inconsistency in the fs. Thoughts?

Brian

Quoted 96 lines

(Maybe I'm missing something?)

--D

Quoted 92 lines

Brian

Quoted 82 lines
fs/xfs/scrub/btree.c +55 -0
+	/* Load the root of the btree. */
+	level = cur->bc_nlevels - 1;
+	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+	error = xfs_scrub_btree_block(&bs, level, &ptr, &block, &bp);
+	if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+		goto out;
+
+	cur->bc_ptrs[level] = 1;
+
+	while (level < cur->bc_nlevels) {
+		block = xfs_btree_get_block(cur, level, &bp);
+
+		if (level == 0) {
+			/* End of leaf, pop back towards the root. */
+			if (cur->bc_ptrs[level] >
+			    be16_to_cpu(block->bb_numrecs)) {
+				if (level < cur->bc_nlevels - 1)
+					cur->bc_ptrs[level + 1]++;
+				level++;
+				continue;
+			}
+
+			if (xfs_scrub_should_terminate(&error))
+				break;
+
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+
+		/* End of node, pop back towards the root. */
+		if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+			if (level < cur->bc_nlevels - 1)
+				cur->bc_ptrs[level + 1]++;
+			level++;
+			continue;
+		}
+
+		/* Drill another level deeper. */
+		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+		error = xfs_scrub_btree_ptr(&bs, level, pp);
+		if (error) {
+			error = 0;
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+		level--;
+		error = xfs_scrub_btree_block(&bs, level, pp, &block, &bp);
+		if (!xfs_scrub_btree_op_ok(sc, cur, level, &error))
+			goto out;
+
+		cur->bc_ptrs[level] = 1;
+	}
+
+out:
+	return error;
 }
fs/xfs/scrub/common.h +13 -1
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
 
--
Quoted 3 lines

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

#29 lore raw

Re: [PATCH 08/27] xfs: scrub the shape of a metadata btree

Quoted 108 lines · Brian Foster
Quoted 107 lines · Darrick J. Wong
Quoted 60 lines · Brian Foster
Quoted 18 lines · Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)

...

Quoted 4 lines
fs/xfs/scrub/btree.c +0 -0
index adf5d09..a9c2bf3 100644

...

Quoted 35 lines
fs/xfs/scrub/btree.c +29 -2
@@ -109,6 +255,92 @@ xfs_scrub_btree(
 	struct xfs_owner_info		*oinfo,
 	void				*private)
 {
-	xfs_scrub_btree_op_ok(sc, cur, 0, false);
-	return -EOPNOTSUPP;
+	struct xfs_scrub_btree		bs = {0};
+	union xfs_btree_ptr		ptr;
+	union xfs_btree_ptr		*pp;
+	struct xfs_btree_block		*block;
+	int				level;
+	struct xfs_buf			*bp;
+	int				i;
+	int				error = 0;
+
+	/* Initialize scrub state */
+	bs.cur = cur;
+	bs.scrub_rec = scrub_fn;
+	bs.oinfo = oinfo;
+	bs.firstrec = true;
+	bs.private = private;
+	bs.sc = sc;
+	for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+		bs.firstkey[i] = true;
+	INIT_LIST_HEAD(&bs.to_check);
+
+	/* Don't try to check a tree with a height we can't handle. */
+	if (!xfs_scrub_btree_check_ok(sc, cur, 0, cur->bc_nlevels > 0 &&
+			cur->bc_nlevels <= XFS_BTREE_MAXLEVELS))
+		goto out;
+
+	/* Make sure the root isn't in the superblock. */
+	if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)) {
+		cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+		error = xfs_scrub_btree_ptr(&bs, cur->bc_nlevels, &ptr);
Quoted 8 lines

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

Quoted 4 lines
fs/xfs/scrub/btree.c +4 -0
+		if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+			goto out;
+	}
+

This is kind of in line with Dave's comments on the previous patch that introduce some of these helpers. I just glanced over them for now because I didn't have enough context to grok the error processing.

(That's been a struggle with this patchset -- some of these helpers don't get used until much further in the patchset. I could have sprinkled them into whichever patch uses them first, but now the hunks are all over the series, I'd have to do more dependency tracking to make sure bisect continues to work, and the frequency of auto-merge failures as I push and pop the stack increase dramatically.)

Quoted 11 lines

FWIW, the btree_op_ok()/btree_check_ok() stuff kind of makes my eyes cross a bit because I can't easily see the logic checks or distinguish between those and error code checks. This is also a bit confusing because it looks like we overload return codes for various things. E.g., we generate -EFSCORRUPTED in some cases just so the caller can set the state on the context and clear it, but then we still use the fact that an error _was_ set to control the flow of the task via the op_ok() return value. This makes some of the code flow/decision making logic hard to follow, particularly since some of that state looks like it can be lost.

Quoted 8 lines

Case in point.. what happens if say xfs_btree_increment() returns -EFSCORRUPTED back to xfs_scrub_btree_block_check_sibling()? It looks to me that the latter calls btree_op_ok() to set the corrupt state, clears the error code and skips out appropriately. xfs_scrub_btree_block_check_sibling() now returns zero, which potentially bubbles up to xfs_scrub_btree() where we check the error code again. Is it expected that error == 0 here? What is supposed to happen here?

Yes, error == 0 is intended here. Given a block B, we want to check that B->rightsib->leftsib (if the sibling exists at all) point back to B. If the btree_increment operation returns EFSCORRUPTED we don't know if that's because the B->rightsib points at something it's not supposed to, or if B->rightsib points at a btree block, but that sibling block is corrupt. Therefore we set the corrupt flag and bubble error == 0 up the
call stack so that we can check the other records in the btree. This
enables those following with ftrace to see everything that scrub thinks is wrong with that piece of metadata.

Quoted 6 lines
Quoted 3 lines

IOWs, we only use error code returns for "runtime error, abort the whole operation immediately".

Ok, so we intentionally have to consume the error here because it doesn't necessarily reflect the corrupted state of the scrubbed block.

Not quite -- while we're examining blocks or otherwise operating on the btree we've been told to scrub, we always want to consume an EFSCORRUPTED error (and set the CORRUPT flag) because we always want to try to check everything, even if we find problems midway through a scan.

This is similar to how gcc will keep processing past the first error to try to report everything that's wrong in the source file instead of bailing out at the first error like it used to do.

Quoted 2 lines

So IIUC, overloading return codes as such means error handling must either return -EFSCORRUPTED for the current object being corrupted,

We /never/ return EFSCORRUPTED to userspace, because we have the OFLAG_CORRUPT flag to indicate any kind of corruption anywhere in this data structure we're checking.

Let's say that a 2-level btree looks like this:

           B0
            |
+-----+-----+--------+-----+----------------+-------------------+
|     |     |        |     |                |                   |
|     |     |        V     |                |                   |
|     |     |  someblock   |                |                   |
|     |     |              |                |                   |
V     V     V              V                V                   V

B1 -> B2 -> B3 ----> B4 -> B5 (badmagic) -> B6 (bad records) -> B7

Here we have a bad pointer in B0 that should point to B4 but now points to something that was never part of B. In B6 we have some incorrect records, and in B5 we have a bad checksum.

First we visit B0. Nothing obviously wrong there, so we proceed with the depth-first search of B. We examine B1 and B2 via pointer[1] and pointer[2], respectively, and find nothing wrong. Now we try pointer[3], which we follow to B3.

Then we get to B3's sibling pointer check. Leftsib is ok, but when we move on to checking rightsib, the xfs_btree_increment returns EFSBADCRC because the pointer[4] in B0 points to a block that isn't in the btree. Here we want to consume the EFSBADCRC, so we set OFLAG_CORRUPT and continue walking the tree.

Next we try to walk pointer[4] in B0 and again hit a EFSBADCRC error. Again we set OFLAG_CORRUPT and continue walking the tree.

Then we try to walk pointer[5] in B0 and encounter B5. The CRC matches, but the magic number is wrong, so we hit EFSCORRUPTED. The block is toast, but we still need to keep walking.

Now we walk pointer[6] and encounter B6. We encounter no operational errors but then we see some incorrect records so we set OFLAG_CORRUPT (it's still set) and continue.

Finally we get to pointer[7], where everything is fine again. If we haven't encountered any operational problems like ENOMEM then we'll return to userspace with OFLAG_CORRUPT set, a return value of zero.

The ftrace buffer will have a report about the operational error trying to walk down pointer[4], another one about B5, record check failures from B6.

(If instead we run out of memory checking B7 then we'll return the ENOMEM to userspace.)

Quoted 40 lines

return some other error for an error in the infrastructure, or clear any -EFSCORRUPTED error generated by checks that don't necessarily mean the current object is corrupted (or too much so to interrupt processing).

Quoted 31 lines
Quoted 17 lines

I'm wondering if this could all be made more clear by trying to explicitly separate out operational errors, scrub failures and whatever we want to call the logic that clears an -EFSCORRUPTED/-EFSBADCRC error code but still indicates something happened. :P

For starters, rather than wrap every logic check with btree_op_check(), could we use explicit logic and let each function update the context based on problems it found? For example, something like the following is much more easy to read for me than the associated logic above:

	/* Don't try to check a tree with a height we can't handle. */
	if (!(cur->bc_nlevels > 0 &&
	      cur->bc_nlevels <= XFS_BTREE_MAXLEVELS)) {
		xfs_scrub_sc_corrupt(...);
		goto out;
	}
Quoted 2 lines

And of course the context update calls could be factored into an out_corrupt label or something where appropriate.

Yes, that could be done.

Quoted 8 lines

Beyond that, where we need to identify a bit of metadata is busted to perhaps do something like skip it but not abort (as we may have filtered out an -EFSCORRUPTED) return code, could we pass a flag down a particular callchain (i.e., think 'bool *bad' or 'int *stat' a la the core btree code)? Then we can still transfer that state back up the chain and the caller(s) can distinguish operational errors from "this thing is corrupted, act accordingly," regardless of how the corruption was detected.

Quoted 4 lines

So far I haven't needed to distinguish between "no problems encountered" and "this callchain hit a verifier error so we just set _CORRUPT" -- scrub always keeps going until it runs out of things to check.

Quoted 6 lines

Ok. This is partly speculation on the above (trying to wrap my head around the error consumption bits as is) and partly to try and see if we can make the flow more readable.

In my mind, this is more clear if return codes are reserved for operational/infrastructure errors

Yes, this is true.

Quoted 1 line

and the corrupted state of a piece of metadata is its own state.

Also true -- this is OFLAG_CORRUPT.

Quoted 3 lines

Using the example above, any -EFSCORRUPTED errors from external calls (xfs_btree_check_block(), xfs_btree_increment(), etc.) would always be cleared and replaced with a return 0.

<nod>

Quoted 3 lines

The difference between those is the former (check_block()) error sets a 'bad = true' state on the currently scrubbed bit of metadata and the latter (check_sibling()) does not.

Nothing in the scrub code needs to track badness at that fine-grained of a level. When we get to the repair patches you'll see that any kind of error triggers a complete rebuild of the btree index, with absolutely no attempt to touch the existing (inconsistent) btree.

We /could/ return to userspace as soon as we hit the first EFSCORRUPTED or failed check, TBH.

Quoted 2 lines

The latter can of course still set the global corrupted state on the context to track that there is an inconsistency in the fs. Thoughts?

I've wondered if it might be clearer if we did something like:

int error;
bool bailout;

error = xfs_btree_increment(...);
bailout = xfs_scrub_op_error(..., &error);
if (bailout)

	return error;
if (ptr->field == BADVAL) {
	xfs_scrub_corrupt(...);
	return error;
}

<shrug>

--D

Quoted 103 lines

Brian

Quoted 96 lines

(Maybe I'm missing something?)

--D

Quoted 92 lines

Brian

Quoted 82 lines
fs/xfs/scrub/btree.c +55 -0
+	/* Load the root of the btree. */
+	level = cur->bc_nlevels - 1;
+	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+	error = xfs_scrub_btree_block(&bs, level, &ptr, &block, &bp);
+	if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+		goto out;
+
+	cur->bc_ptrs[level] = 1;
+
+	while (level < cur->bc_nlevels) {
+		block = xfs_btree_get_block(cur, level, &bp);
+
+		if (level == 0) {
+			/* End of leaf, pop back towards the root. */
+			if (cur->bc_ptrs[level] >
+			    be16_to_cpu(block->bb_numrecs)) {
+				if (level < cur->bc_nlevels - 1)
+					cur->bc_ptrs[level + 1]++;
+				level++;
+				continue;
+			}
+
+			if (xfs_scrub_should_terminate(&error))
+				break;
+
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+
+		/* End of node, pop back towards the root. */
+		if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+			if (level < cur->bc_nlevels - 1)
+				cur->bc_ptrs[level + 1]++;
+			level++;
+			continue;
+		}
+
+		/* Drill another level deeper. */
+		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+		error = xfs_scrub_btree_ptr(&bs, level, pp);
+		if (error) {
+			error = 0;
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+		level--;
+		error = xfs_scrub_btree_block(&bs, level, pp, &block, &bp);
+		if (!xfs_scrub_btree_op_ok(sc, cur, level, &error))
+			goto out;
+
+		cur->bc_ptrs[level] = 1;
+	}
+
+out:
+	return error;
 }
fs/xfs/scrub/common.h +13 -1
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
 
--
Quoted 3 lines

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

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

#30 lore raw

Re: [PATCH 08/27] xfs: scrub the shape of a metadata btree

Quoted 113 lines · Darrick J. Wong
Quoted 112 lines · Brian Foster
Quoted 110 lines · Darrick J. Wong
Quoted 60 lines · Brian Foster
Quoted 18 lines · Darrick J. Wong

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

Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.

---
 fs/xfs/libxfs/xfs_btree.c |   16 +++
 fs/xfs/libxfs/xfs_btree.h |    7 +
 fs/xfs/scrub/btree.c      |  236 +++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h     |   13 ++
 4 files changed, 268 insertions(+), 4 deletions(-)

...

Quoted 4 lines
fs/xfs/scrub/btree.c +0 -0
index adf5d09..a9c2bf3 100644

...

Quoted 35 lines
fs/xfs/scrub/btree.c +29 -2
@@ -109,6 +255,92 @@ xfs_scrub_btree(
 	struct xfs_owner_info		*oinfo,
 	void				*private)
 {
-	xfs_scrub_btree_op_ok(sc, cur, 0, false);
-	return -EOPNOTSUPP;
+	struct xfs_scrub_btree		bs = {0};
+	union xfs_btree_ptr		ptr;
+	union xfs_btree_ptr		*pp;
+	struct xfs_btree_block		*block;
+	int				level;
+	struct xfs_buf			*bp;
+	int				i;
+	int				error = 0;
+
+	/* Initialize scrub state */
+	bs.cur = cur;
+	bs.scrub_rec = scrub_fn;
+	bs.oinfo = oinfo;
+	bs.firstrec = true;
+	bs.private = private;
+	bs.sc = sc;
+	for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+		bs.firstkey[i] = true;
+	INIT_LIST_HEAD(&bs.to_check);
+
+	/* Don't try to check a tree with a height we can't handle. */
+	if (!xfs_scrub_btree_check_ok(sc, cur, 0, cur->bc_nlevels > 0 &&
+			cur->bc_nlevels <= XFS_BTREE_MAXLEVELS))
+		goto out;
+
+	/* Make sure the root isn't in the superblock. */
+	if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)) {
+		cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+		error = xfs_scrub_btree_ptr(&bs, cur->bc_nlevels, &ptr);
Quoted 8 lines

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

Quoted 4 lines
fs/xfs/scrub/btree.c +4 -0
+		if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+			goto out;
+	}
+

This is kind of in line with Dave's comments on the previous patch that introduce some of these helpers. I just glanced over them for now because I didn't have enough context to grok the error processing.

(That's been a struggle with this patchset -- some of these helpers don't get used until much further in the patchset. I could have sprinkled them into whichever patch uses them first, but now the hunks are all over the series, I'd have to do more dependency tracking to make sure bisect continues to work, and the frequency of auto-merge failures as I push and pop the stack increase dramatically.)

Quoted 11 lines

FWIW, the btree_op_ok()/btree_check_ok() stuff kind of makes my eyes cross a bit because I can't easily see the logic checks or distinguish between those and error code checks. This is also a bit confusing because it looks like we overload return codes for various things. E.g., we generate -EFSCORRUPTED in some cases just so the caller can set the state on the context and clear it, but then we still use the fact that an error _was_ set to control the flow of the task via the op_ok() return value. This makes some of the code flow/decision making logic hard to follow, particularly since some of that state looks like it can be lost.

Quoted 8 lines

Case in point.. what happens if say xfs_btree_increment() returns -EFSCORRUPTED back to xfs_scrub_btree_block_check_sibling()? It looks to me that the latter calls btree_op_ok() to set the corrupt state, clears the error code and skips out appropriately. xfs_scrub_btree_block_check_sibling() now returns zero, which potentially bubbles up to xfs_scrub_btree() where we check the error code again. Is it expected that error == 0 here? What is supposed to happen here?

Yes, error == 0 is intended here. Given a block B, we want to check that B->rightsib->leftsib (if the sibling exists at all) point back to B. If the btree_increment operation returns EFSCORRUPTED we don't know if that's because the B->rightsib points at something it's not supposed to, or if B->rightsib points at a btree block, but that sibling block is corrupt. Therefore we set the corrupt flag and bubble error == 0 up the
call stack so that we can check the other records in the btree. This
enables those following with ftrace to see everything that scrub thinks is wrong with that piece of metadata.

IOWs, we only use error code returns for "runtime error, abort the whole operation immediately".

Quoted 8 lines
Quoted 2 lines

Ok, so we intentionally have to consume the error here because it doesn't necessarily reflect the corrupted state of the scrubbed block.

Not quite -- while we're examining blocks or otherwise operating on the btree we've been told to scrub, we always want to consume an EFSCORRUPTED error (and set the CORRUPT flag) because we always want to try to check everything, even if we find problems midway through a scan.

Then why don't we consume -EFSCORRUPTED errors from the call to xfs_btree_check_block() in xfs_scrub_btree_block(), but leave it for the caller? Note that the point I'm trying to get at is not to say that we sometimes don't consume -EFSCORRUPTED errors at all, but rather we consume them in different places to implicitly affect the code flow.

In the xfs_btree_check_block() case, my understanding is that we have to leave that one for the caller to consume rather than consume it immediately like we do in the check_sibling() code. If my understand is not correct, what's the difference between those two sites with regard to consuming -EFSCORRUPTED immediately in one and not the other?

Quoted 11 lines

This is similar to how gcc will keep processing past the first error to try to report everything that's wrong in the source file instead of bailing out at the first error like it used to do.

Quoted 2 lines

So IIUC, overloading return codes as such means error handling must either return -EFSCORRUPTED for the current object being corrupted,

We /never/ return EFSCORRUPTED to userspace, because we have the OFLAG_CORRUPT flag to indicate any kind of corruption anywhere in this data structure we're checking.

Understood, I'm not suggesting we'd return -EFSCORRUPTED to userspace.

Quoted 42 lines

Let's say that a 2-level btree looks like this:

           B0
            |
+-----+-----+--------+-----+----------------+-------------------+
|     |     |        |     |                |                   |
|     |     |        V     |                |                   |
|     |     |  someblock   |                |                   |
|     |     |              |                |                   |
V     V     V              V                V                   V

B1 -> B2 -> B3 ----> B4 -> B5 (badmagic) -> B6 (bad records) -> B7

Here we have a bad pointer in B0 that should point to B4 but now points to something that was never part of B. In B6 we have some incorrect records, and in B5 we have a bad checksum.

First we visit B0. Nothing obviously wrong there, so we proceed with the depth-first search of B. We examine B1 and B2 via pointer[1] and pointer[2], respectively, and find nothing wrong. Now we try pointer[3], which we follow to B3.

Then we get to B3's sibling pointer check. Leftsib is ok, but when we move on to checking rightsib, the xfs_btree_increment returns EFSBADCRC because the pointer[4] in B0 points to a block that isn't in the btree. Here we want to consume the EFSBADCRC, so we set OFLAG_CORRUPT and continue walking the tree.

Next we try to walk pointer[4] in B0 and again hit a EFSBADCRC error. Again we set OFLAG_CORRUPT and continue walking the tree.

Then we try to walk pointer[5] in B0 and encounter B5. The CRC matches, but the magic number is wrong, so we hit EFSCORRUPTED. The block is toast, but we still need to keep walking.

Now we walk pointer[6] and encounter B6. We encounter no operational errors but then we see some incorrect records so we set OFLAG_CORRUPT (it's still set) and continue.

Finally we get to pointer[7], where everything is fine again. If we haven't encountered any operational problems like ENOMEM then we'll return to userspace with OFLAG_CORRUPT set, a return value of zero.

Quoted 7 lines

The ftrace buffer will have a report about the operational error trying to walk down pointer[4], another one about B5, record check failures from B6.

(If instead we run out of memory checking B7 then we'll return the ENOMEM to userspace.)

Got it, thanks. Note that I'm not trying to suggest to deviate from this behavior. It's just a refactoring such that -EFSCORRUPTED is always consumed immediately from any external function that could generate it and is translated into the local scrub state (i.e., corrupted = true and/or OFLAG_CORRUPT, where the latter is a subset of the former). Basically, think of it as instead of never returning -EFSCORRUPTED to userspace, it should not be returned by any scrub function, ever.

Quoted 59 lines
Quoted 40 lines

return some other error for an error in the infrastructure, or clear any -EFSCORRUPTED error generated by checks that don't necessarily mean the current object is corrupted (or too much so to interrupt processing).

Quoted 31 lines
Quoted 17 lines

I'm wondering if this could all be made more clear by trying to explicitly separate out operational errors, scrub failures and whatever we want to call the logic that clears an -EFSCORRUPTED/-EFSBADCRC error code but still indicates something happened. :P

For starters, rather than wrap every logic check with btree_op_check(), could we use explicit logic and let each function update the context based on problems it found? For example, something like the following is much more easy to read for me than the associated logic above:

	/* Don't try to check a tree with a height we can't handle. */
	if (!(cur->bc_nlevels > 0 &&
	      cur->bc_nlevels <= XFS_BTREE_MAXLEVELS)) {
		xfs_scrub_sc_corrupt(...);
		goto out;
	}
Quoted 2 lines

And of course the context update calls could be factored into an out_corrupt label or something where appropriate.

Yes, that could be done.

Quoted 8 lines

Beyond that, where we need to identify a bit of metadata is busted to perhaps do something like skip it but not abort (as we may have filtered out an -EFSCORRUPTED) return code, could we pass a flag down a particular callchain (i.e., think 'bool *bad' or 'int *stat' a la the core btree code)? Then we can still transfer that state back up the chain and the caller(s) can distinguish operational errors from "this thing is corrupted, act accordingly," regardless of how the corruption was detected.

Quoted 4 lines

So far I haven't needed to distinguish between "no problems encountered" and "this callchain hit a verifier error so we just set _CORRUPT" -- scrub always keeps going until it runs out of things to check.

Quoted 6 lines

Ok. This is partly speculation on the above (trying to wrap my head around the error consumption bits as is) and partly to try and see if we can make the flow more readable.

In my mind, this is more clear if return codes are reserved for operational/infrastructure errors

Yes, this is true.

Quoted 1 line

and the corrupted state of a piece of metadata is its own state.

Also true -- this is OFLAG_CORRUPT.

Quoted 3 lines

Using the example above, any -EFSCORRUPTED errors from external calls (xfs_btree_check_block(), xfs_btree_increment(), etc.) would always be cleared and replaced with a return 0.

<nod>

Quoted 9 lines
Quoted 3 lines

The difference between those is the former (check_block()) error sets a 'bad = true' state on the currently scrubbed bit of metadata and the latter (check_sibling()) does not.

Nothing in the scrub code needs to track badness at that fine-grained of a level. When we get to the repair patches you'll see that any kind of error triggers a complete rebuild of the btree index, with absolutely no attempt to touch the existing (inconsistent) btree.

Perhaps, but that's not really the point. My argument is that it simplifies the logic and thus makes the code more clear. :)

Quoted 3 lines

We /could/ return to userspace as soon as we hit the first EFSCORRUPTED or failed check, TBH.

That's a separate question that I haven't really thought much about yet tbh. I suppose I could see use for a oneshot option or something for users who may want to exit quickly in favor of offline repair rather than wait for a thorough scan.

Quoted 11 lines
Quoted 2 lines

The latter can of course still set the global corrupted state on the context to track that there is an inconsistency in the fs. Thoughts?

I've wondered if it might be clearer if we did something like:

int error;
bool bailout;

error = xfs_btree_increment(...);
bailout = xfs_scrub_op_error(..., &error);
if (bailout)

Quoted 7 lines
	return error;
if (ptr->field == BADVAL) {
	xfs_scrub_corrupt(...);
	return error;
}

It still appears confusing to me because isn't whatever we were going to do that depends on the xfs_btree_increment() call now bogus if the increment itself fails, for whatever reason? When would you not bail out of here on increment failure?

Using a more simple example of xfs_scrub_btree_block(), I'd expect it to look something like this:

xfs_scrub_btree_block(..., bool *bad, ...)

{
	...

	error = xfs_btree_lookup_get_block(bs->cur, ...);
	if (error)
		goto out_err;

	xfs_btree_get_block(bs->cur, ...);
	error = xfs_btree_check_block(bs->cur, ...);
	if (error)
		goto out_err;

	error = xfs_scrub_btree_block_check_siblings(bs, ...);
	ASSERT(error != -EFSCORRUPTED);
	return error;

out_err:

	if (error == -EFSCORRUPTED) {
		*bad = true;
		error = 0;
	}
	return error;
}

check_siblings() doesn't need bad because it's checking the siblings. It just sets OFLAG_CORRUPT if it finds corruption or returns a non corruption error if one occurs. The caller sets OFLAG_CORRUPT if bad == true or bails if error != 0.

Brian

Quoted 107 lines

<shrug>

--D

Quoted 103 lines

Brian

Quoted 96 lines

(Maybe I'm missing something?)

--D

Quoted 92 lines

Brian

Quoted 82 lines
fs/xfs/scrub/btree.c +55 -0
+	/* Load the root of the btree. */
+	level = cur->bc_nlevels - 1;
+	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+	error = xfs_scrub_btree_block(&bs, level, &ptr, &block, &bp);
+	if (!xfs_scrub_btree_op_ok(sc, cur, cur->bc_nlevels - 1, &error))
+		goto out;
+
+	cur->bc_ptrs[level] = 1;
+
+	while (level < cur->bc_nlevels) {
+		block = xfs_btree_get_block(cur, level, &bp);
+
+		if (level == 0) {
+			/* End of leaf, pop back towards the root. */
+			if (cur->bc_ptrs[level] >
+			    be16_to_cpu(block->bb_numrecs)) {
+				if (level < cur->bc_nlevels - 1)
+					cur->bc_ptrs[level + 1]++;
+				level++;
+				continue;
+			}
+
+			if (xfs_scrub_should_terminate(&error))
+				break;
+
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+
+		/* End of node, pop back towards the root. */
+		if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+			if (level < cur->bc_nlevels - 1)
+				cur->bc_ptrs[level + 1]++;
+			level++;
+			continue;
+		}
+
+		/* Drill another level deeper. */
+		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+		error = xfs_scrub_btree_ptr(&bs, level, pp);
+		if (error) {
+			error = 0;
+			cur->bc_ptrs[level]++;
+			continue;
+		}
+		level--;
+		error = xfs_scrub_btree_block(&bs, level, pp, &block, &bp);
+		if (!xfs_scrub_btree_op_ok(sc, cur, level, &error))
+			goto out;
+
+		cur->bc_ptrs[level] = 1;
+	}
+
+out:
+	return error;
 }
fs/xfs/scrub/common.h +13 -1
index e1bb14b..9920488 100644
@@ -20,6 +20,19 @@
 #ifndef __XFS_SCRUB_COMMON_H__
 #define __XFS_SCRUB_COMMON_H__
 
+/* Should we end the scrub early? */
+static inline bool
+xfs_scrub_should_terminate(
+	int		*error)
+{
+	if (fatal_signal_pending(current)) {
+		if (*error == 0)
+			*error = -EAGAIN;
+		return true;
+	}
+	return false;
+}
+
 /*
  * Grab a transaction.  If we're going to repair something, we need to
  * ensure there's enough reservation to make all the changes.  If not,
 
--
Quoted 3 lines

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

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

#31 lore raw

PATCH v10 9/27 xfs: scrub btree keys and records

In reply to Darrick J. Wong

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

Add to the btree scrubber the ability to check that the keys and records are in the right order and actually call out to our record iterator to do actual checking of the records.

---
 fs/xfs/scrub/btree.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/trace.h |   44 +++++++++++++++++++
 2 files changed, 159 insertions(+)
fs/xfs/scrub/btree.c +115 -0
index a9c2bf3..0ae56f5 100644
@@ -94,6 +94,104 @@ xfs_scrub_btree_check_ok(
 	return fs_ok;
 }
 
+/*
+ * Make sure this record is in order and doesn't stray outside of the parent
+ * keys.
+ */
+STATIC int
+xfs_scrub_btree_rec(
+	struct xfs_scrub_btree	*bs)
+{
+	struct xfs_btree_cur	*cur = bs->cur;
+	union xfs_btree_rec	*rec;
+	union xfs_btree_key	key;
+	union xfs_btree_key	hkey;
+	union xfs_btree_key	*keyp;
+	struct xfs_btree_block	*block;
+	struct xfs_btree_block	*keyblock;
+	struct xfs_buf		*bp;
+
+	block = xfs_btree_get_block(cur, 0, &bp);
+	rec = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
+
+	trace_xfs_scrub_btree_rec(bs->sc, cur, 0);
+
+	/* If this isn't the first record, are they in order? */
+	xfs_scrub_btree_check_ok(bs->sc, cur, 0, bs->firstrec ||
+			cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec));
+	bs->firstrec = false;
+	memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
+
+	if (cur->bc_nlevels == 1)
+		return 0;
+
+	/* Is this at least as large as the parent low key? */
+	cur->bc_ops->init_key_from_rec(&key, rec);
+	keyblock = xfs_btree_get_block(cur, 1, &bp);
+	keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[1], keyblock);
+	xfs_scrub_btree_check_ok(bs->sc, cur, 1,
+			cur->bc_ops->diff_two_keys(cur, &key, keyp) >= 0);
+
+	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
+		return 0;
+
+	/* Is this no larger than the parent high key? */
+	cur->bc_ops->init_high_key_from_rec(&hkey, rec);
+	keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[1], keyblock);
+	xfs_scrub_btree_check_ok(bs->sc, cur, 1,
+			cur->bc_ops->diff_two_keys(cur, keyp, &hkey) >= 0);
+
+	return 0;
+}
+
+/*
+ * Make sure this key is in order and doesn't stray outside of the parent
+ * keys.
+ */
+STATIC int
+xfs_scrub_btree_key(
+	struct xfs_scrub_btree	*bs,
+	int			level)
+{
+	struct xfs_btree_cur	*cur = bs->cur;
+	union xfs_btree_key	*key;
+	union xfs_btree_key	*keyp;
+	struct xfs_btree_block	*block;
+	struct xfs_btree_block	*keyblock;
+	struct xfs_buf		*bp;
+
+	block = xfs_btree_get_block(cur, level, &bp);
+	key = xfs_btree_key_addr(cur, cur->bc_ptrs[level], block);
+
+	trace_xfs_scrub_btree_key(bs->sc, cur, level);
+
+	/* If this isn't the first key, are they in order? */
+	xfs_scrub_btree_check_ok(bs->sc, cur, level, bs->firstkey[level] ||
+			cur->bc_ops->keys_inorder(cur, &bs->lastkey[level], key));
+	bs->firstkey[level] = false;
+	memcpy(&bs->lastkey[level], key, cur->bc_ops->key_len);
+
+	if (level + 1 >= cur->bc_nlevels)
+		return 0;
+
+	/* Is this at least as large as the parent low key? */
+	keyblock = xfs_btree_get_block(cur, level + 1, &bp);
+	keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
+	xfs_scrub_btree_check_ok(bs->sc, cur, level,
+			cur->bc_ops->diff_two_keys(cur, key, keyp) >= 0);
+
+	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
+		return 0;
+
+	/* Is this no larger than the parent high key? */
+	key = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level], block);
+	keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
+	xfs_scrub_btree_check_ok(bs->sc, cur, level,
+			cur->bc_ops->diff_two_keys(cur, keyp, key) >= 0);
+
+	return 0;
+}
+
 /* Check a btree pointer. */
 static int
 xfs_scrub_btree_ptr(
@@ -258,6 +356,7 @@ xfs_scrub_btree(
 	struct xfs_scrub_btree		bs = {0};
 	union xfs_btree_ptr		ptr;
 	union xfs_btree_ptr		*pp;
+	union xfs_btree_rec		*recp;
 	struct xfs_btree_block		*block;
 	int				level;
 	struct xfs_buf			*bp;
@@ -310,6 +409,17 @@ xfs_scrub_btree(
 				continue;
 			}
 
+			/* Records in order for scrub? */
+			error = xfs_scrub_btree_rec(&bs);
+			if (error)
+				goto out;
+
+			/* Call out to the record checker. */
+			recp = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
+			error = bs.scrub_rec(&bs, recp);
+			if (error < 0 ||
+			    error == XFS_BTREE_QUERY_RANGE_ABORT)
+				break;
 			if (xfs_scrub_should_terminate(&error))
 				break;
 
@@ -325,6 +435,11 @@ xfs_scrub_btree(
 			continue;
 		}
 
+		/* Keys in order for scrub? */
+		error = xfs_scrub_btree_key(&bs, level);
+		if (error)
+			goto out;
+
 		/* Drill another level deeper. */
 		pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
 		error = xfs_scrub_btree_ptr(&bs, level, pp);
fs/xfs/scrub/trace.h +44 -0

50 lines not on this page · raw message

#32 lore raw

PATCH v10 10/27 xfs: create helpers to scan an allocation group

In reply to Darrick J. Wong

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

Add some helpers to enable us to lock an AG's headers, create btree cursors for all btrees in that allocation group, and clean up afterwards.

---
 fs/xfs/scrub/common.c |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h |   10 +++
 fs/xfs/scrub/scrub.c  |    4 +
 fs/xfs/scrub/scrub.h  |   21 ++++++
 4 files changed, 208 insertions(+)
fs/xfs/scrub/common.c +173 -0
index cf3f1365..1e80f23 100644
@@ -44,6 +44,7 @@
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
+#include "scrub/btree.h"
 
 /* Common code for the metadata scrubbers. */
 
@@ -290,6 +291,178 @@ xfs_scrub_check_thoroughness(
 	return fs_ok;
 }
 
+/*
+ * AG scrubbing
+ *
+ * These helpers facilitate locking an allocation group's header
+ * buffers, setting up cursors for all btrees that are present, and
+ * cleaning everything up once we're through.
+ */
+
+/* Grab all the headers for an AG. */
+int
+xfs_scrub_ag_read_headers(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	struct xfs_buf			**agi,
+	struct xfs_buf			**agf,
+	struct xfs_buf			**agfl)
+{
+	struct xfs_mount		*mp = sc->mp;
+	int				error;
+
+	error = xfs_ialloc_read_agi(mp, sc->tp, agno, agi);
+	if (error)
+		goto out;
+
+	error = xfs_alloc_read_agf(mp, sc->tp, agno, 0, agf);
+	if (error)
+		goto out;
+	if (!*agf) {
+		error = -ENOMEM;
+		goto out;
+	}
+
+	error = xfs_alloc_read_agfl(mp, sc->tp, agno, agfl);
+	if (error)
+		goto out;
+
+out:
+	return error;
+}
+
+/* Release all the AG btree cursors. */
+void
+xfs_scrub_ag_btcur_free(
+	struct xfs_scrub_ag		*sa)
+{
+	if (sa->refc_cur)
+		xfs_btree_del_cursor(sa->refc_cur, XFS_BTREE_ERROR);
+	if (sa->rmap_cur)
+		xfs_btree_del_cursor(sa->rmap_cur, XFS_BTREE_ERROR);
+	if (sa->fino_cur)
+		xfs_btree_del_cursor(sa->fino_cur, XFS_BTREE_ERROR);
+	if (sa->ino_cur)
+		xfs_btree_del_cursor(sa->ino_cur, XFS_BTREE_ERROR);
+	if (sa->cnt_cur)
+		xfs_btree_del_cursor(sa->cnt_cur, XFS_BTREE_ERROR);
+	if (sa->bno_cur)
+		xfs_btree_del_cursor(sa->bno_cur, XFS_BTREE_ERROR);
+
+	sa->refc_cur = NULL;
+	sa->rmap_cur = NULL;
+	sa->fino_cur = NULL;
+	sa->ino_cur = NULL;
+	sa->bno_cur = NULL;
+	sa->cnt_cur = NULL;
+}
+
+/* Initialize all the btree cursors for an AG. */
+int
+xfs_scrub_ag_btcur_init(
+	struct xfs_scrub_context	*sc,
+	struct xfs_scrub_ag		*sa)
+{
+	struct xfs_mount		*mp = sc->mp;
+	xfs_agnumber_t			agno = sa->agno;
+
+	if (sa->agf_bp) {
+		/* Set up a bnobt cursor for cross-referencing. */
+		sa->bno_cur = xfs_allocbt_init_cursor(mp, sc->tp, sa->agf_bp,
+				agno, XFS_BTNUM_BNO);
+		if (!sa->bno_cur)
+			goto err;
+
+		/* Set up a cntbt cursor for cross-referencing. */
+		sa->cnt_cur = xfs_allocbt_init_cursor(mp, sc->tp, sa->agf_bp,
+				agno, XFS_BTNUM_CNT);
+		if (!sa->cnt_cur)
+			goto err;
+	}
+
+	/* Set up a inobt cursor for cross-referencing. */
+	if (sa->agi_bp) {
+		sa->ino_cur = xfs_inobt_init_cursor(mp, sc->tp, sa->agi_bp,
+					agno, XFS_BTNUM_INO);
+		if (!sa->ino_cur)
+			goto err;
+	}
+
+	/* Set up a finobt cursor for cross-referencing. */
+	if (sa->agi_bp && xfs_sb_version_hasfinobt(&mp->m_sb)) {
+		sa->fino_cur = xfs_inobt_init_cursor(mp, sc->tp, sa->agi_bp,
+				agno, XFS_BTNUM_FINO);
+		if (!sa->fino_cur)
+			goto err;
+	}
+
+	/* Set up a rmapbt cursor for cross-referencing. */
+	if (sa->agf_bp && xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		sa->rmap_cur = xfs_rmapbt_init_cursor(mp, sc->tp, sa->agf_bp,
+				agno);
+		if (!sa->rmap_cur)
+			goto err;
+	}
+
+	/* Set up a refcountbt cursor for cross-referencing. */
+	if (sa->agf_bp && xfs_sb_version_hasreflink(&mp->m_sb)) {
+		sa->refc_cur = xfs_refcountbt_init_cursor(mp, sc->tp,
+				sa->agf_bp, agno, NULL);
+		if (!sa->refc_cur)
+			goto err;
+	}
+
+	return 0;
+err:
+	return -ENOMEM;
+}
+
+/* Release the AG header context and btree cursors. */
+void
+xfs_scrub_ag_free(
+	struct xfs_scrub_context	*sc,
+	struct xfs_scrub_ag		*sa)
+{
+	xfs_scrub_ag_btcur_free(sa);
+	if (sa->agfl_bp) {
+		xfs_trans_brelse(sc->tp, sa->agfl_bp);
+		sa->agfl_bp = NULL;
+	}
+	if (sa->agf_bp) {
+		xfs_trans_brelse(sc->tp, sa->agf_bp);
+		sa->agf_bp = NULL;
+	}
+	if (sa->agi_bp) {
+		xfs_trans_brelse(sc->tp, sa->agi_bp);
+		sa->agi_bp = NULL;
+	}
+	sa->agno = NULLAGNUMBER;
+}
+
+/*
+ * For scrub, grab the AGI and the AGF headers, in that order.  Locking
+ * order requires us to get the AGI before the AGF.  We use the
+ * transaction to avoid deadlocking on crosslinked metadata buffers;
+ * either the caller passes one in (bmap scrub) or we have to create a
+ * transaction ourselves.
+ */
+int
+xfs_scrub_ag_init(
+	struct xfs_scrub_context	*sc,
+	xfs_agnumber_t			agno,
+	struct xfs_scrub_ag		*sa)
+{
+	int				error;
+
+	sa->agno = agno;
+	error = xfs_scrub_ag_read_headers(sc, agno, &sa->agi_bp,
+			&sa->agf_bp, &sa->agfl_bp);
+	if (error)
+		return error;
+
+	return xfs_scrub_ag_btcur_init(sc, sa);
+}
+
 /* Per-scrubber setup functions */
 
 /* Set us up with a transaction and an empty context. */
fs/xfs/scrub/common.h +10 -0
index 9920488..105b7ad 100644
@@ -93,4 +93,14 @@ bool xfs_scrub_check_thoroughness(struct xfs_scrub_context *sc, bool fs_ok);
 /* Setup functions */
 int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
 
+void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
+int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
+		      struct xfs_scrub_ag *sa);
+int xfs_scrub_ag_read_headers(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
+			      struct xfs_buf **agi, struct xfs_buf **agf,
+			      struct xfs_buf **agfl);
+void xfs_scrub_ag_btcur_free(struct xfs_scrub_ag *sa);
+int xfs_scrub_ag_btcur_init(struct xfs_scrub_context *sc,
+			    struct xfs_scrub_ag *sa);
+
 #endif	/* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +4 -0
index 7936a23..bfc53b6 100644
@@ -44,6 +44,8 @@
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/trace.h"
+#include "scrub/scrub.h"
+#include "scrub/btree.h"
 
 /*
  * Online Scrub and Repair
@@ -145,6 +147,7 @@ xfs_scrub_teardown(
 	struct xfs_scrub_context	*sc,
 	int				error)
 {
+	xfs_scrub_ag_free(sc, &sc->sa);
 	if (sc->tp) {
 		xfs_trans_cancel(sc->tp);
 		sc->tp = NULL;
@@ -225,6 +228,7 @@ xfs_scrub_metadata(
 	sc.sm = sm;
 	sc.ops = ops;
 	sc.try_harder = try_harder;
+	sc.sa.agno = NULLAGNUMBER;
 	error = sc.ops->setup(&sc, ip);
 	if (error)
 		goto out_teardown;
fs/xfs/scrub/scrub.h +21 -0
index 2528039..291444f 100644
@@ -29,6 +29,24 @@ struct xfs_scrub_meta_ops {
 	bool		(*has)(struct xfs_sb *);
 };
 
+/* Buffer pointers and btree cursors for an entire AG. */
+struct xfs_scrub_ag {
+	xfs_agnumber_t			agno;
+
+	/* AG btree roots */
+	struct xfs_buf			*agf_bp;
+	struct xfs_buf			*agfl_bp;
+	struct xfs_buf			*agi_bp;
+
+	/* AG btrees */
+	struct xfs_btree_cur		*bno_cur;
+	struct xfs_btree_cur		*cnt_cur;
+	struct xfs_btree_cur		*ino_cur;
+	struct xfs_btree_cur		*fino_cur;
+	struct xfs_btree_cur		*rmap_cur;
+	struct xfs_btree_cur		*refc_cur;
+};
+
 struct xfs_scrub_context {
 	/* General scrub state. */
 	struct xfs_mount		*mp;
@@ -37,6 +55,9 @@ struct xfs_scrub_context {
 	struct xfs_trans		*tp;
 	struct xfs_inode		*ip;
 	bool				try_harder;
+
+	/* State tracking for single-AG operations. */
+	struct xfs_scrub_ag		sa;
 };
 
 /* Metadata scrubbers */
#33 lore raw

PATCH v10 11/27 xfs: scrub the backup superblocks

In reply to Darrick J. Wong

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

Ensure that the geometry presented in the backup superblocks matches the primary superblock so that repair can recover the filesystem if that primary gets corrupted.

---
 fs/xfs/Makefile         |    1 
 fs/xfs/libxfs/xfs_fs.h  |    3 
 fs/xfs/scrub/agheader.c |  326 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h   |    2 
 fs/xfs/scrub/scrub.c    |    4 +
 fs/xfs/scrub/scrub.h    |    1 
 6 files changed, 336 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/agheader.c
fs/xfs/Makefile +1 -0
index 5888b9f..e92d04d 100644
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
+				   agheader.o \
 				   btree.o \
 				   common.o \
 				   scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
index 5105bad..b98bba2 100644
@@ -484,9 +484,10 @@ struct xfs_scrub_metadata {
 
 /* Scrub subcommands. */
 #define XFS_SCRUB_TYPE_TEST	0	/* presence test ioctl */
+#define XFS_SCRUB_TYPE_SB	1	/* superblock */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	1
+#define XFS_SCRUB_TYPE_NR	2
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/agheader.c new file +326 -0

326 lines not on this page · raw message

fs/xfs/scrub/common.h +2 -0
index 105b7ad..4c7c308 100644
@@ -92,6 +92,8 @@ bool xfs_scrub_check_thoroughness(struct xfs_scrub_context *sc, bool fs_ok);
 
 /* Setup functions */
 int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
+int xfs_scrub_setup_ag_header(struct xfs_scrub_context *sc,
+			      struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/scrub.c +4 -0
index bfc53b6..8e08224 100644
@@ -162,6 +162,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_fs,
 		.scrub	= xfs_scrub_tester,
 	},
+	{ /* superblock */
+		.setup	= xfs_scrub_setup_ag_header,
+		.scrub	= xfs_scrub_superblock,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index 291444f..027f62e 100644
@@ -62,5 +62,6 @@ struct xfs_scrub_context {
 
 /* Metadata scrubbers */
 int xfs_scrub_tester(struct xfs_scrub_context *sc);
+int xfs_scrub_superblock(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#34 lore raw

PATCH v10 12/27 xfs: scrub AGF and AGFL

In reply to Darrick J. Wong

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

Check the block references in the AGF and AGFL headers to make sure they make sense.

---
 fs/xfs/libxfs/xfs_fs.h  |    4 +
 fs/xfs/scrub/agheader.c |  220 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.c   |   60 +++++++++++++
 fs/xfs/scrub/common.h   |    6 +
 fs/xfs/scrub/scrub.c    |    8 ++
 fs/xfs/scrub/scrub.h    |    2 
 6 files changed, 299 insertions(+), 1 deletion(-)
fs/xfs/libxfs/xfs_fs.h +3 -1
index b98bba2..a22d90f 100644
@@ -485,9 +485,11 @@ struct xfs_scrub_metadata {
 /* Scrub subcommands. */
 #define XFS_SCRUB_TYPE_TEST	0	/* presence test ioctl */
 #define XFS_SCRUB_TYPE_SB	1	/* superblock */
+#define XFS_SCRUB_TYPE_AGF	2	/* AG free header */
+#define XFS_SCRUB_TYPE_AGFL	3	/* AG free list */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	2
+#define XFS_SCRUB_TYPE_NR	4
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/agheader.c +220 -0

229 lines not on this page · raw message

fs/xfs/scrub/common.c +60 -0

66 lines not on this page · raw message

fs/xfs/scrub/common.h +6 -0
index 4c7c308..4d9a56c 100644
@@ -104,5 +104,11 @@ int xfs_scrub_ag_read_headers(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
 void xfs_scrub_ag_btcur_free(struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_btcur_init(struct xfs_scrub_context *sc,
 			    struct xfs_scrub_ag *sa);
+int xfs_scrub_load_ag_headers(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
+			      unsigned int type);
+int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
+			int (*fn)(struct xfs_scrub_context *, xfs_agblock_t bno,
+				  void *),
+			void *priv);
 
 #endif	/* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +8 -0
index 8e08224..1afce7b 100644
@@ -166,6 +166,14 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_ag_header,
 		.scrub	= xfs_scrub_superblock,
 	},
+	{ /* agf */
+		.setup	= xfs_scrub_setup_ag_header,
+		.scrub	= xfs_scrub_agf,
+	},
+	{ /* agfl */
+		.setup	= xfs_scrub_setup_ag_header,
+		.scrub	= xfs_scrub_agfl,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +2 -0
index 027f62e..cb31a01 100644
@@ -63,5 +63,7 @@ struct xfs_scrub_context {
 /* Metadata scrubbers */
 int xfs_scrub_tester(struct xfs_scrub_context *sc);
 int xfs_scrub_superblock(struct xfs_scrub_context *sc);
+int xfs_scrub_agf(struct xfs_scrub_context *sc);
+int xfs_scrub_agfl(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#35 lore raw

PATCH v10 13/27 xfs: scrub the AGI

In reply to Darrick J. Wong

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

Add a forgotten check to the AGI verifier, then wire up the scrub infrastructure to check the AGI contents.

---
 fs/xfs/libxfs/xfs_fs.h  |    3 +-
 fs/xfs/scrub/agheader.c |   88 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.c   |    6 ++-
 fs/xfs/scrub/scrub.c    |    4 ++
 fs/xfs/scrub/scrub.h    |    1 +
 5 files changed, 99 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_fs.h +2 -1
index a22d90f..2ac0049 100644
@@ -487,9 +487,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_SB	1	/* superblock */
 #define XFS_SCRUB_TYPE_AGF	2	/* AG free header */
 #define XFS_SCRUB_TYPE_AGFL	3	/* AG free list */
+#define XFS_SCRUB_TYPE_AGI	4	/* AG inode header */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	4
+#define XFS_SCRUB_TYPE_NR	5
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/agheader.c +88 -0

91 lines not on this page · raw message

fs/xfs/scrub/common.c +4 -2
index e0e611d..b62f084 100644
@@ -499,12 +499,14 @@ xfs_scrub_load_ag_headers(
 	struct xfs_mount		*mp = sc->mp;
 	int				error;
 
-	ASSERT(type == XFS_SCRUB_TYPE_AGF || type == XFS_SCRUB_TYPE_AGFL);
+	ASSERT(type == XFS_SCRUB_TYPE_AGF || type == XFS_SCRUB_TYPE_AGFL ||
+	       type == XFS_SCRUB_TYPE_AGI);
 	memset(&sc->sa, 0, sizeof(sc->sa));
 	sc->sa.agno = agno;
 
 	error = xfs_scrub_load_ag_header(sc, XFS_AGI_DADDR(mp),
-			&sc->sa.agi_bp, &xfs_agi_buf_ops, false);
+			&sc->sa.agi_bp, &xfs_agi_buf_ops,
+			type == XFS_SCRUB_TYPE_AGI);
 	if (error)
 		return error;
 
fs/xfs/scrub/scrub.c +4 -0
index 1afce7b..5fc34a6 100644
@@ -174,6 +174,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_ag_header,
 		.scrub	= xfs_scrub_agfl,
 	},
+	{ /* agi */
+		.setup	= xfs_scrub_setup_ag_header,
+		.scrub	= xfs_scrub_agi,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index cb31a01..00020a3 100644
@@ -65,5 +65,6 @@ int xfs_scrub_tester(struct xfs_scrub_context *sc);
 int xfs_scrub_superblock(struct xfs_scrub_context *sc);
 int xfs_scrub_agf(struct xfs_scrub_context *sc);
 int xfs_scrub_agfl(struct xfs_scrub_context *sc);
+int xfs_scrub_agi(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#36 lore raw

PATCH v10 14/27 xfs: scrub free space btrees

In reply to Darrick J. Wong

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

Check the extent records free space btrees to ensure that the values look sane.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    4 +-
 fs/xfs/scrub/alloc.c   |  108 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.c  |   16 +++++++
 fs/xfs/scrub/common.h  |    6 +++
 fs/xfs/scrub/scrub.c   |    8 ++++
 fs/xfs/scrub/scrub.h   |    2 +
 7 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/alloc.c
fs/xfs/Makefile +1 -0
index e92d04d..84ac733 100644
@@ -147,6 +147,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
 				   agheader.o \
+				   alloc.o \
 				   btree.o \
 				   common.o \
 				   scrub.o \
fs/xfs/libxfs/xfs_fs.h +3 -1
index 2ac0049..55230b8 100644
@@ -488,9 +488,11 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_AGF	2	/* AG free header */
 #define XFS_SCRUB_TYPE_AGFL	3	/* AG free list */
 #define XFS_SCRUB_TYPE_AGI	4	/* AG inode header */
+#define XFS_SCRUB_TYPE_BNOBT	5	/* freesp by block btree */
+#define XFS_SCRUB_TYPE_CNTBT	6	/* freesp by length btree */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	5
+#define XFS_SCRUB_TYPE_NR	7
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/alloc.c new file +108 -0

108 lines not on this page · raw message

fs/xfs/scrub/common.c +16 -0
index b62f084..a3e185c 100644
@@ -536,3 +536,19 @@ xfs_scrub_setup_fs(
 	return xfs_scrub_trans_alloc(sc->sm, sc->mp,
 			&M_RES(sc->mp)->tr_itruncate, 0, 0, 0, &sc->tp);
 }
+
+/* Set us up with AG headers and btree cursors. */
+int
+xfs_scrub_setup_ag_btree(
+	struct xfs_scrub_context	*sc,
+	struct xfs_inode		*ip,
+	bool				force_log)
+{
+	int				error;
+
+	error = xfs_scrub_setup_ag_header(sc, ip);
+	if (error)
+		return error;
+
+	return xfs_scrub_ag_init(sc, sc->sm->sm_agno, &sc->sa);
+}
fs/xfs/scrub/common.h +6 -0
index 4d9a56c..d70f470 100644
@@ -94,6 +94,9 @@ bool xfs_scrub_check_thoroughness(struct xfs_scrub_context *sc, bool fs_ok);
 int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
 int xfs_scrub_setup_ag_header(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
+int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc,
+			       struct xfs_inode *ip);
+
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
@@ -111,4 +114,7 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
 				  void *),
 			void *priv);
 
+int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc,
+			     struct xfs_inode *ip, bool force_log);
+
 #endif	/* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +8 -0
index 5fc34a6..e41778c 100644
@@ -178,6 +178,14 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_ag_header,
 		.scrub	= xfs_scrub_agi,
 	},
+	{ /* bnobt */
+		.setup	= xfs_scrub_setup_ag_allocbt,
+		.scrub	= xfs_scrub_bnobt,
+	},
+	{ /* cntbt */
+		.setup	= xfs_scrub_setup_ag_allocbt,
+		.scrub	= xfs_scrub_cntbt,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +2 -0
index 00020a3..3f1a77c 100644
@@ -66,5 +66,7 @@ int xfs_scrub_superblock(struct xfs_scrub_context *sc);
 int xfs_scrub_agf(struct xfs_scrub_context *sc);
 int xfs_scrub_agfl(struct xfs_scrub_context *sc);
 int xfs_scrub_agi(struct xfs_scrub_context *sc);
+int xfs_scrub_bnobt(struct xfs_scrub_context *sc);
+int xfs_scrub_cntbt(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#37 lore raw

PATCH v10 15/27 xfs: scrub inode btrees

In reply to Darrick J. Wong

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

Check the records of the inode btrees to make sure that the values make sense given the inode records themselves.

---
 fs/xfs/Makefile            |    1 
 fs/xfs/libxfs/xfs_format.h |    2 
 fs/xfs/libxfs/xfs_fs.h     |    4 -
 fs/xfs/scrub/common.h      |    2 
 fs/xfs/scrub/ialloc.c      |  332 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c       |    9 +
 fs/xfs/scrub/scrub.h       |    2 
 7 files changed, 350 insertions(+), 2 deletions(-)
 create mode 100644 fs/xfs/scrub/ialloc.c
fs/xfs/Makefile +1 -0
index 84ac733..82326b7 100644
@@ -150,6 +150,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   alloc.o \
 				   btree.o \
 				   common.o \
+				   ialloc.o \
 				   scrub.o \
 				   )
 endif
fs/xfs/libxfs/xfs_format.h +1 -1
index 23229f0..154c3dd 100644
@@ -518,7 +518,7 @@ static inline int xfs_sb_version_hasftype(struct xfs_sb *sbp)
 		 (sbp->sb_features2 & XFS_SB_VERSION2_FTYPE));
 }
 
-static inline int xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
+static inline bool xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
 {
 	return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
 		(sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT);
fs/xfs/libxfs/xfs_fs.h +3 -1
index 55230b8..c8e6d89 100644
@@ -490,9 +490,11 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_AGI	4	/* AG inode header */
 #define XFS_SCRUB_TYPE_BNOBT	5	/* freesp by block btree */
 #define XFS_SCRUB_TYPE_CNTBT	6	/* freesp by length btree */
+#define XFS_SCRUB_TYPE_INOBT	7	/* inode btree */
+#define XFS_SCRUB_TYPE_FINOBT	8	/* free inode btree */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	7
+#define XFS_SCRUB_TYPE_NR	9
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +2 -0
index d70f470..8fc0619 100644
@@ -96,6 +96,8 @@ int xfs_scrub_setup_ag_header(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
 int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc,
 			       struct xfs_inode *ip);
+int xfs_scrub_setup_ag_iallocbt(struct xfs_scrub_context *sc,
+				struct xfs_inode *ip);
 
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
fs/xfs/scrub/ialloc.c new file +332 -0

332 lines not on this page · raw message

fs/xfs/scrub/scrub.c +9 -0
index e41778c..9304bb3 100644
@@ -186,6 +186,15 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_ag_allocbt,
 		.scrub	= xfs_scrub_cntbt,
 	},
+	{ /* inobt */
+		.setup	= xfs_scrub_setup_ag_iallocbt,
+		.scrub	= xfs_scrub_inobt,
+	},
+	{ /* finobt */
+		.setup	= xfs_scrub_setup_ag_iallocbt,
+		.scrub	= xfs_scrub_finobt,
+		.has	= xfs_sb_version_hasfinobt,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +2 -0
index 3f1a77c..55c9dde 100644
@@ -68,5 +68,7 @@ int xfs_scrub_agfl(struct xfs_scrub_context *sc);
 int xfs_scrub_agi(struct xfs_scrub_context *sc);
 int xfs_scrub_bnobt(struct xfs_scrub_context *sc);
 int xfs_scrub_cntbt(struct xfs_scrub_context *sc);
+int xfs_scrub_inobt(struct xfs_scrub_context *sc);
+int xfs_scrub_finobt(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#38 lore raw

PATCH v10 16/27 xfs: scrub rmap btrees

In reply to Darrick J. Wong

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

Check the reverse mapping records to make sure that the contents make sense.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    3 +
 fs/xfs/scrub/common.h  |    2 +
 fs/xfs/scrub/rmap.c    |  130 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |    5 ++
 fs/xfs/scrub/scrub.h   |    1 
 6 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/rmap.c
fs/xfs/Makefile +1 -0
index 82326b7..5a64f8d 100644
@@ -151,6 +151,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   btree.o \
 				   common.o \
 				   ialloc.o \
+				   rmap.o \
 				   scrub.o \
 				   )
 endif
fs/xfs/libxfs/xfs_fs.h +2 -1
index c8e6d89..74aa021 100644
@@ -492,9 +492,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_CNTBT	6	/* freesp by length btree */
 #define XFS_SCRUB_TYPE_INOBT	7	/* inode btree */
 #define XFS_SCRUB_TYPE_FINOBT	8	/* free inode btree */
+#define XFS_SCRUB_TYPE_RMAPBT	9	/* reverse mapping btree */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	9
+#define XFS_SCRUB_TYPE_NR	10
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +2 -0
index 8fc0619..9075a5c 100644
@@ -98,6 +98,8 @@ int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc,
 			       struct xfs_inode *ip);
 int xfs_scrub_setup_ag_iallocbt(struct xfs_scrub_context *sc,
 				struct xfs_inode *ip);
+int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc,
+			      struct xfs_inode *ip);
 
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
fs/xfs/scrub/rmap.c new file +130 -0

130 lines not on this page · raw message

fs/xfs/scrub/scrub.c +5 -0
index 9304bb3..a418269 100644
@@ -195,6 +195,11 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.scrub	= xfs_scrub_finobt,
 		.has	= xfs_sb_version_hasfinobt,
 	},
+	{ /* rmapbt */
+		.setup	= xfs_scrub_setup_ag_rmapbt,
+		.scrub	= xfs_scrub_rmapbt,
+		.has	= xfs_sb_version_hasrmapbt,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index 55c9dde..f602ded 100644
@@ -70,5 +70,6 @@ int xfs_scrub_bnobt(struct xfs_scrub_context *sc);
 int xfs_scrub_cntbt(struct xfs_scrub_context *sc);
 int xfs_scrub_inobt(struct xfs_scrub_context *sc);
 int xfs_scrub_finobt(struct xfs_scrub_context *sc);
+int xfs_scrub_rmapbt(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#39 lore raw

PATCH v10 17/27 xfs: scrub refcount btrees

In reply to Darrick J. Wong

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

Plumb in the pieces necessary to check the refcount btree. If rmap is available, check the reference count by performing an interval query against the rmapbt.

---
 fs/xfs/Makefile         |    1 
 fs/xfs/libxfs/xfs_fs.h  |    3 +
 fs/xfs/scrub/common.h   |    2 +
 fs/xfs/scrub/refcount.c |   99 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c    |    5 ++
 fs/xfs/scrub/scrub.h    |    1 
 6 files changed, 110 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/refcount.c
fs/xfs/Makefile +1 -0
index 5a64f8d..a7c5752 100644
@@ -151,6 +151,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   btree.o \
 				   common.o \
 				   ialloc.o \
+				   refcount.o \
 				   rmap.o \
 				   scrub.o \
 				   )
fs/xfs/libxfs/xfs_fs.h +2 -1
index 74aa021..450a692 100644
@@ -493,9 +493,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_INOBT	7	/* inode btree */
 #define XFS_SCRUB_TYPE_FINOBT	8	/* free inode btree */
 #define XFS_SCRUB_TYPE_RMAPBT	9	/* reverse mapping btree */
+#define XFS_SCRUB_TYPE_REFCNTBT	10	/* reference count btree */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	10
+#define XFS_SCRUB_TYPE_NR	11
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +2 -0
index 9075a5c..e50368d 100644
@@ -100,6 +100,8 @@ int xfs_scrub_setup_ag_iallocbt(struct xfs_scrub_context *sc,
 				struct xfs_inode *ip);
 int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
+int xfs_scrub_setup_ag_refcountbt(struct xfs_scrub_context *sc,
+				  struct xfs_inode *ip);
 
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
fs/xfs/scrub/refcount.c new file +99 -0

99 lines not on this page · raw message

fs/xfs/scrub/scrub.c +5 -0
index a418269..4af2096 100644
@@ -200,6 +200,11 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.scrub	= xfs_scrub_rmapbt,
 		.has	= xfs_sb_version_hasrmapbt,
 	},
+	{ /* refcountbt */
+		.setup	= xfs_scrub_setup_ag_refcountbt,
+		.scrub	= xfs_scrub_refcountbt,
+		.has	= xfs_sb_version_hasreflink,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index f602ded..4cc4b84 100644
@@ -71,5 +71,6 @@ int xfs_scrub_cntbt(struct xfs_scrub_context *sc);
 int xfs_scrub_inobt(struct xfs_scrub_context *sc);
 int xfs_scrub_finobt(struct xfs_scrub_context *sc);
 int xfs_scrub_rmapbt(struct xfs_scrub_context *sc);
+int xfs_scrub_refcountbt(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#40 lore raw

PATCH v10 18/27 xfs: scrub inodes

In reply to Darrick J. Wong

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

Scrub the fields within an inode.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    3 
 fs/xfs/scrub/common.c  |   49 +++++++
 fs/xfs/scrub/common.h  |    3 
 fs/xfs/scrub/inode.c   |  342 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |   17 ++
 fs/xfs/scrub/scrub.h   |    2 
 7 files changed, 414 insertions(+), 3 deletions(-)
 create mode 100644 fs/xfs/scrub/inode.c
fs/xfs/Makefile +1 -0
index a7c5752..28e14b7 100644
@@ -151,6 +151,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   btree.o \
 				   common.o \
 				   ialloc.o \
+				   inode.o \
 				   refcount.o \
 				   rmap.o \
 				   scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
index 450a692..2051c3d 100644
@@ -494,9 +494,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_FINOBT	8	/* free inode btree */
 #define XFS_SCRUB_TYPE_RMAPBT	9	/* reverse mapping btree */
 #define XFS_SCRUB_TYPE_REFCNTBT	10	/* reference count btree */
+#define XFS_SCRUB_TYPE_INODE	11	/* inode record */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	11
+#define XFS_SCRUB_TYPE_NR	12
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.c +49 -0

58 lines not on this page · raw message

fs/xfs/scrub/common.h +3 -0
index e50368d..ae6b557 100644
@@ -102,6 +102,8 @@ int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
 int xfs_scrub_setup_ag_refcountbt(struct xfs_scrub_context *sc,
 				  struct xfs_inode *ip);
+int xfs_scrub_setup_inode(struct xfs_scrub_context *sc,
+			  struct xfs_inode *ip);
 
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
@@ -122,5 +124,6 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
 
 int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc,
 			     struct xfs_inode *ip, bool force_log);
+int xfs_scrub_get_inode(struct xfs_scrub_context *sc, struct xfs_inode *ip_in);
 
 #endif	/* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/inode.c new file +342 -0

342 lines not on this page · raw message

fs/xfs/scrub/scrub.c +15 -2
index 4af2096..b88f86e 100644
@@ -30,6 +30,8 @@
 #include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_itable.h"
 #include "xfs_alloc.h"
 #include "xfs_alloc_btree.h"
 #include "xfs_bmap.h"
@@ -145,6 +147,7 @@ xfs_scrub_tester(
 STATIC int
 xfs_scrub_teardown(
 	struct xfs_scrub_context	*sc,
+	struct xfs_inode		*ip_in,
 	int				error)
 {
 	xfs_scrub_ag_free(sc, &sc->sa);
@@ -152,6 +155,12 @@ xfs_scrub_teardown(
 		xfs_trans_cancel(sc->tp);
 		sc->tp = NULL;
 	}
+	if (sc->ip) {
+		xfs_iunlock(sc->ip, sc->ilock_flags);
+		if (sc->ip != ip_in)
+			iput(VFS_I(sc->ip));
+		sc->ip = NULL;
+	}
 	return error;
 }
 
@@ -205,6 +214,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.scrub	= xfs_scrub_refcountbt,
 		.has	= xfs_sb_version_hasreflink,
 	},
+	{ /* inode record */
+		.setup	= xfs_scrub_setup_inode,
+		.scrub	= xfs_scrub_inode,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
@@ -284,7 +297,7 @@ xfs_scrub_metadata(
 		 * Tear down everything we hold, then set up again with
 		 * preparation for worst-case scenarios.
 		 */
-		error = xfs_scrub_teardown(&sc, 0);
+		error = xfs_scrub_teardown(&sc, ip, 0);
 		if (error)
 			goto out;
 		try_harder = true;
@@ -297,7 +310,7 @@ xfs_scrub_metadata(
 		xfs_alert_ratelimited(mp, "Corruption detected during scrub.");
 
 out_teardown:
-	error = xfs_scrub_teardown(&sc, error);
+	error = xfs_scrub_teardown(&sc, ip, error);
 out:
 	trace_xfs_scrub_done(ip, sm, error);
 	return error;
fs/xfs/scrub/scrub.h +2 -0
index 4cc4b84..fed8ed1 100644
@@ -54,6 +54,7 @@ struct xfs_scrub_context {
 	const struct xfs_scrub_meta_ops	*ops;
 	struct xfs_trans		*tp;
 	struct xfs_inode		*ip;
+	uint				ilock_flags;
 	bool				try_harder;
 
 	/* State tracking for single-AG operations. */
@@ -72,5 +73,6 @@ int xfs_scrub_inobt(struct xfs_scrub_context *sc);
 int xfs_scrub_finobt(struct xfs_scrub_context *sc);
 int xfs_scrub_rmapbt(struct xfs_scrub_context *sc);
 int xfs_scrub_refcountbt(struct xfs_scrub_context *sc);
+int xfs_scrub_inode(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#41 lore raw

PATCH v10 19/27 xfs: scrub inode block mappings

In reply to Darrick J. Wong

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

Scrub an individual inode's block mappings to make sure they make sense.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    5 +
 fs/xfs/scrub/bmap.c    |  358 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h  |    5 +
 fs/xfs/scrub/scrub.c   |   12 ++
 fs/xfs/scrub/scrub.h   |    3 
 6 files changed, 382 insertions(+), 2 deletions(-)
 create mode 100644 fs/xfs/scrub/bmap.c
fs/xfs/Makefile +1 -0
index 28e14b7..5a77489 100644
@@ -148,6 +148,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
 				   agheader.o \
 				   alloc.o \
+				   bmap.o \
 				   btree.o \
 				   common.o \
 				   ialloc.o \
fs/xfs/libxfs/xfs_fs.h +4 -1
index 2051c3d..27e9f90 100644
@@ -495,9 +495,12 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_RMAPBT	9	/* reverse mapping btree */
 #define XFS_SCRUB_TYPE_REFCNTBT	10	/* reference count btree */
 #define XFS_SCRUB_TYPE_INODE	11	/* inode record */
+#define XFS_SCRUB_TYPE_BMBTD	12	/* data fork block mapping */
+#define XFS_SCRUB_TYPE_BMBTA	13	/* attr fork block mapping */
+#define XFS_SCRUB_TYPE_BMBTC	14	/* CoW fork block mapping */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	12
+#define XFS_SCRUB_TYPE_NR	15
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/bmap.c new file +358 -0

358 lines not on this page · raw message

fs/xfs/scrub/common.h +4 -1
index ae6b557..79c00ad 100644
@@ -104,7 +104,10 @@ int xfs_scrub_setup_ag_refcountbt(struct xfs_scrub_context *sc,
 				  struct xfs_inode *ip);
 int xfs_scrub_setup_inode(struct xfs_scrub_context *sc,
 			  struct xfs_inode *ip);
-
+int xfs_scrub_setup_inode_bmap(struct xfs_scrub_context *sc,
+			       struct xfs_inode *ip);
+int xfs_scrub_setup_inode_bmap_data(struct xfs_scrub_context *sc,
+				    struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/scrub.c +12 -0
index b88f86e..cd896e5 100644
@@ -218,6 +218,18 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_inode,
 		.scrub	= xfs_scrub_inode,
 	},
+	{ /* inode data fork */
+		.setup	= xfs_scrub_setup_inode_bmap_data,
+		.scrub	= xfs_scrub_bmap_data,
+	},
+	{ /* inode attr fork */
+		.setup	= xfs_scrub_setup_inode_bmap,
+		.scrub	= xfs_scrub_bmap_attr,
+	},
+	{ /* inode CoW fork */
+		.setup	= xfs_scrub_setup_inode_bmap,
+		.scrub	= xfs_scrub_bmap_cow,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +3 -0
index fed8ed1..75323e4 100644
@@ -74,5 +74,8 @@ int xfs_scrub_finobt(struct xfs_scrub_context *sc);
 int xfs_scrub_rmapbt(struct xfs_scrub_context *sc);
 int xfs_scrub_refcountbt(struct xfs_scrub_context *sc);
 int xfs_scrub_inode(struct xfs_scrub_context *sc);
+int xfs_scrub_bmap_data(struct xfs_scrub_context *sc);
+int xfs_scrub_bmap_attr(struct xfs_scrub_context *sc);
+int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#42 lore raw

PATCH v10 20/27 xfs: scrub directory/attribute btrees

In reply to Darrick J. Wong

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

Provide a way to check the shape and scrub the hashes and records in a directory or extended attribute btree. These are helper functions for the directory & attribute scrubbers in subsequent patches.

[fengguang: remove unneeded variable to store return value]
---
 fs/xfs/Makefile        |    1 
 fs/xfs/scrub/dabtree.c |  539 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/dabtree.h |   52 +++++
 3 files changed, 592 insertions(+)
 create mode 100644 fs/xfs/scrub/dabtree.c
 create mode 100644 fs/xfs/scrub/dabtree.h
fs/xfs/Makefile +1 -0
index 5a77489..b48437f 100644
@@ -151,6 +151,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   bmap.o \
 				   btree.o \
 				   common.o \
+				   dabtree.o \
 				   ialloc.o \
 				   inode.o \
 				   refcount.o \
fs/xfs/scrub/dabtree.c new file +539 -0

539 lines not on this page · raw message

fs/xfs/scrub/dabtree.h new file +52 -0

52 lines not on this page · raw message

#43 lore raw

PATCH v10 21/27 xfs: scrub directory metadata

In reply to Darrick J. Wong

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

Scrub the hash tree and all the entries in a directory.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    3 
 fs/xfs/scrub/common.c  |   33 +++++
 fs/xfs/scrub/common.h  |    4 +
 fs/xfs/scrub/dir.c     |  289 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |    4 +
 fs/xfs/scrub/scrub.h   |    1 
 7 files changed, 334 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/dir.c
fs/xfs/Makefile +1 -0
index b48437f..69aa88e 100644
@@ -152,6 +152,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   btree.o \
 				   common.o \
 				   dabtree.o \
+				   dir.o \
 				   ialloc.o \
 				   inode.o \
 				   refcount.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
index 27e9f90..8d2bea5 100644
@@ -498,9 +498,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_BMBTD	12	/* data fork block mapping */
 #define XFS_SCRUB_TYPE_BMBTA	13	/* attr fork block mapping */
 #define XFS_SCRUB_TYPE_BMBTC	14	/* CoW fork block mapping */
+#define XFS_SCRUB_TYPE_DIR	15	/* directory */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	15
+#define XFS_SCRUB_TYPE_NR	16
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.c +33 -0
index 2a1d456..a332610 100644
@@ -601,3 +601,36 @@ xfs_scrub_get_inode(
 	sc->ip = ips;
 	return 0;
 }
+
+/* Set us up to scrub a file's contents. */
+int
+xfs_scrub_setup_inode_contents(
+	struct xfs_scrub_context	*sc,
+	struct xfs_inode		*ip,
+	unsigned int			resblks)
+{
+	struct xfs_mount		*mp = sc->mp;
+	int				error;
+
+	error = xfs_scrub_get_inode(sc, ip);
+	if (error)
+		return error;
+
+	/* Got the inode, lock it and we're ready to go. */
+	sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
+	xfs_ilock(sc->ip, sc->ilock_flags);
+	error = xfs_scrub_trans_alloc(sc->sm, mp, &M_RES(mp)->tr_itruncate,
+			resblks, 0, 0, &sc->tp);
+	if (error)
+		goto out_unlock;
+	sc->ilock_flags |= XFS_ILOCK_EXCL;
+	xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
+
+	return 0;
+out_unlock:
+	xfs_iunlock(sc->ip, sc->ilock_flags);
+	if (sc->ip != ip)
+		iput(VFS_I(sc->ip));
+	sc->ip = NULL;
+	return error;
+}
fs/xfs/scrub/common.h +4 -0
index 79c00ad..82b8056 100644
@@ -108,6 +108,8 @@ int xfs_scrub_setup_inode_bmap(struct xfs_scrub_context *sc,
 			       struct xfs_inode *ip);
 int xfs_scrub_setup_inode_bmap_data(struct xfs_scrub_context *sc,
 				    struct xfs_inode *ip);
+int xfs_scrub_setup_directory(struct xfs_scrub_context *sc,
+			      struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
@@ -128,5 +130,7 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,
 int xfs_scrub_setup_ag_btree(struct xfs_scrub_context *sc,
 			     struct xfs_inode *ip, bool force_log);
 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);
 
 #endif	/* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/dir.c new file +289 -0

289 lines not on this page · raw message

fs/xfs/scrub/scrub.c +4 -0
index cd896e5..760492a 100644
@@ -230,6 +230,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_inode_bmap,
 		.scrub	= xfs_scrub_bmap_cow,
 	},
+	{ /* directory */
+		.setup	= xfs_scrub_setup_directory,
+		.scrub	= xfs_scrub_directory,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index 75323e4..4c348e8 100644
@@ -77,5 +77,6 @@ int xfs_scrub_inode(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_data(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_attr(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);
+int xfs_scrub_directory(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#44 lore raw

PATCH v10 22/27 xfs: scrub directory freespace

In reply to Darrick J. Wong

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

Check the free space information in a directory.

---
 fs/xfs/scrub/dir.c |  339 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 339 insertions(+)
fs/xfs/scrub/dir.c +339 -0
index 57adca5..54d20e7 100644
@@ -229,6 +229,340 @@ xfs_scrub_dir_rec(
 	return error;
 }
 
+/* Is this free entry either in the bestfree or smaller than all of them? */
+static inline bool
+xfs_scrub_directory_check_free_entry(
+	struct xfs_dir2_data_free	*bf,
+	struct xfs_dir2_data_unused	*dup)
+{
+	struct xfs_dir2_data_free	*dfp;
+	unsigned int			smallest;
+
+	smallest = -1U;
+	for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+		if (dfp->offset &&
+		    be16_to_cpu(dfp->length) == be16_to_cpu(dup->length))
+			return true;
+		if (smallest < be16_to_cpu(dfp->length))
+			smallest = be16_to_cpu(dfp->length);
+	}
+
+	return be16_to_cpu(dup->length) <= smallest;
+}
+
+/* Check free space info in a directory data block. */
+STATIC int
+xfs_scrub_directory_data_bestfree(
+	struct xfs_scrub_context	*sc,
+	xfs_dablk_t			lblk,
+	bool				is_block)
+{
+	struct xfs_dir2_data_unused	*dup;
+	struct xfs_dir2_data_free	*dfp;
+	struct xfs_buf			*bp;
+	struct xfs_dir2_data_free	*bf;
+	struct xfs_mount		*mp = sc->mp;
+	char				*ptr;
+	char				*endptr;
+	u16				tag;
+	int				newlen;
+	int				offset;
+	int				error;
+
+	if (is_block) {
+		/* dir block format */
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk, lblk ==
+				XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET));
+		error = xfs_dir3_block_read(sc->tp, sc->ip, &bp);
+	} else {
+		/* dir data format */
+		error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, -1, &bp);
+	}
+	if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+		goto out;
+
+	/* Do the bestfrees correspond to actual free space? */
+	bf = sc->ip->d_ops->data_bestfree_p(bp->b_addr);
+	for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+		offset = be16_to_cpu(dfp->offset);
+		if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				offset < BBTOB(bp->b_length)) || !offset)
+			continue;
+		dup = (struct xfs_dir2_data_unused *)(bp->b_addr + offset);
+		tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
+
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				dup->freetag ==
+					cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) &&
+				be16_to_cpu(dup->length) ==
+					be16_to_cpu(dfp->length) &&
+				tag == ((char *)dup - (char *)bp->b_addr));
+	}
+
+	/* Make sure the bestfrees are actually the best free spaces. */
+	ptr = (char *)sc->ip->d_ops->data_entry_p(bp->b_addr);
+	if (is_block) {
+		struct xfs_dir2_block_tail	*btp;
+
+		btp = xfs_dir2_block_tail_p(sc->mp->m_dir_geo, bp->b_addr);
+		endptr = (char *)xfs_dir2_block_leaf_p(btp);
+	} else
+		endptr = (char *)bp->b_addr + BBTOB(bp->b_length);
+	while (ptr < endptr) {
+		dup = (struct xfs_dir2_data_unused *)ptr;
+		/* Skip real entries */
+		if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
+			struct xfs_dir2_data_entry	*dep;
+
+			dep = (struct xfs_dir2_data_entry *)ptr;
+			newlen = sc->ip->d_ops->data_entsize(dep->namelen);
+			if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+					newlen > 0))
+				goto out_buf;
+			ptr += newlen;
+			xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+					ptr <= endptr);
+			continue;
+		}
+
+		/* Spot check this free entry */
+		tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				tag == ((char *)dup - (char *)bp->b_addr));
+
+		/*
+		 * Either this entry is a bestfree or it's smaller than
+		 * any of the bestfrees.
+		 */
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				xfs_scrub_directory_check_free_entry(bf, dup));
+
+		/* Move on. */
+		newlen = be16_to_cpu(dup->length);
+		if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				newlen > 0))
+			goto out_buf;
+		ptr += newlen;
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				ptr <= endptr);
+	}
+out_buf:
+	xfs_trans_brelse(sc->tp, bp);
+out:
+	return error;
+}
+
+/* Is this the longest free entry in the block? */
+static inline bool
+xfs_scrub_directory_check_freesp(
+	struct xfs_inode		*dp,
+	struct xfs_buf			*dbp,
+	unsigned int			len)
+{
+	struct xfs_dir2_data_free	*bf;
+	struct xfs_dir2_data_free	*dfp;
+	unsigned int			longest = 0;
+	int				offset;
+
+	bf = dp->d_ops->data_bestfree_p(dbp->b_addr);
+	for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+		offset = be16_to_cpu(dfp->offset);
+		if (!offset)
+			continue;
+		if (longest < be16_to_cpu(dfp->length))
+			longest = be16_to_cpu(dfp->length);
+	}
+
+	return longest == len;
+}
+
+/* Check free space info in a directory leaf1 block. */
+STATIC int
+xfs_scrub_directory_leaf1_bestfree(
+	struct xfs_scrub_context	*sc,
+	struct xfs_da_args		*args,
+	xfs_dablk_t			lblk)
+{
+	struct xfs_dir2_leaf_tail	*ltp;
+	struct xfs_buf			*dbp;
+	struct xfs_buf			*bp;
+	struct xfs_mount		*mp = sc->mp;
+	__be16				*bestp;
+	__u16				best;
+	int				i;
+	int				error;
+
+	/* Read the free space block */
+	error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp);
+	if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+		goto out;
+
+	/* Check all the entries. */
+	ltp = xfs_dir2_leaf_tail_p(mp->m_dir_geo, bp->b_addr);
+	bestp = xfs_dir2_leaf_bests_p(ltp);
+	for (i = 0; i < be32_to_cpu(ltp->bestcount); i++, bestp++) {
+		best = be16_to_cpu(*bestp);
+		if (best == NULLDATAOFF)
+			continue;
+		error = xfs_dir3_data_read(sc->tp, sc->ip,
+				i * args->geo->fsbcount, -1, &dbp);
+		if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+			continue;
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				xfs_scrub_directory_check_freesp(sc->ip, dbp,
+					best));
+		xfs_trans_brelse(sc->tp, dbp);
+	}
+out:
+	return error;
+}
+
+/* Check free space info in a directory freespace block. */
+STATIC int
+xfs_scrub_directory_free_bestfree(
+	struct xfs_scrub_context	*sc,
+	struct xfs_da_args		*args,
+	xfs_dablk_t			lblk)
+{
+	struct xfs_dir3_icfree_hdr	freehdr;
+	struct xfs_buf			*dbp;
+	struct xfs_buf			*bp;
+	__be16				*bestp;
+	__be16				best;
+	int				i;
+	int				error;
+
+	/* Read the free space block */
+	error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);
+	if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+		goto out;
+
+	/* Check all the entries. */
+	sc->ip->d_ops->free_hdr_from_disk(&freehdr, bp->b_addr);
+	bestp = sc->ip->d_ops->free_bests_p(bp->b_addr);
+	for (i = 0; i < freehdr.nvalid; i++, bestp++) {
+		best = be16_to_cpu(*bestp);
+		if (best == NULLDATAOFF)
+			continue;
+		error = xfs_dir3_data_read(sc->tp, sc->ip,
+				(freehdr.firstdb + i) * args->geo->fsbcount,
+				-1, &dbp);
+		if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+			continue;
+		xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				xfs_scrub_directory_check_freesp(sc->ip, dbp,
+					best));
+		xfs_trans_brelse(sc->tp, dbp);
+	}
+out:
+	return error;
+}
+
+/* Check free space information in directories. */
+STATIC int
+xfs_scrub_directory_blocks(
+	struct xfs_scrub_context	*sc)
+{
+	struct xfs_bmbt_irec		got;
+	struct xfs_da_args		args;
+	struct xfs_ifork		*ifp;
+	struct xfs_mount		*mp = sc->mp;
+	xfs_fileoff_t			leaf_lblk;
+	xfs_fileoff_t			free_lblk;
+	xfs_fileoff_t			lblk;
+	xfs_extnum_t			idx;
+	bool				found;
+	int				is_block = 0;
+	int				error;
+
+	/* Ignore local format directories. */
+	if (sc->ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+	    sc->ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
+		return 0;
+
+	ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
+	lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
+	leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
+	free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
+
+	/* Is this a block dir? */
+	args.dp = sc->ip;
+	args.geo = mp->m_dir_geo;
+	args.trans = sc->tp;
+	error = xfs_dir2_isblock(&args, &is_block);
+	if (!xfs_scrub_fblock_op_ok(sc, XFS_DATA_FORK, lblk, &error))
+		goto out;
+
+	/* Iterate all the data extents in the directory... */
+	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+	while (found) {
+		/* No more data blocks... */
+		if (got.br_startoff >= leaf_lblk)
+			break;
+
+		/* Check each data block's bestfree data */
+		for (lblk = roundup((xfs_dablk_t)got.br_startoff,
+				args.geo->fsbcount);
+		     lblk < got.br_startoff + got.br_blockcount;
+		     lblk += args.geo->fsbcount) {
+			error = xfs_scrub_directory_data_bestfree(sc, lblk,
+					is_block);
+			if (error)
+				goto out;
+		}
+
+		found = xfs_iext_get_extent(ifp, ++idx, &got);
+	}
+
+	/* Look for a leaf1 block, which has free info. */
+	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &idx, &got) &&
+	    got.br_startoff == leaf_lblk &&
+	    got.br_blockcount == args.geo->fsbcount &&
+	    !xfs_iext_get_extent(ifp, ++idx, &got)) {
+		if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				!is_block))
+			goto not_leaf1;
+		error = xfs_scrub_directory_leaf1_bestfree(sc, &args,
+				leaf_lblk);
+		if (error)
+			goto out;
+	}
+not_leaf1:
+
+	/* Scan for free blocks */
+	lblk = free_lblk;
+	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+	while (found) {
+		/*
+		 * Dirs can't have blocks mapped above 2^32.
+		 * Single-block dirs shouldn't even be here.
+		 */
+		lblk = got.br_startoff;
+		if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				!(lblk & ~((1ULL << 32) - 1ULL))))
+			goto out;
+		if (!xfs_scrub_fblock_check_ok(sc, XFS_DATA_FORK, lblk,
+				!is_block))
+			goto nextfree;
+
+		/* Check each dir free block's bestfree data */
+		for (lblk = roundup((xfs_dablk_t)got.br_startoff,
+				args.geo->fsbcount);
+		     lblk < got.br_startoff + got.br_blockcount;
+		     lblk += args.geo->fsbcount) {
+			error = xfs_scrub_directory_free_bestfree(sc, &args,
+					lblk);
+			if (error)
+				goto out;
+		}
+
+nextfree:
+		found = xfs_iext_get_extent(ifp, ++idx, &got);
+	}
+out:
+	return error;
+}
+
 /* Scrub a whole directory. */
 int
 xfs_scrub_directory(
@@ -255,6 +589,11 @@ xfs_scrub_directory(
 	if (error)
 		return error;
 
+	/* Check the freespace. */
+	error = xfs_scrub_directory_blocks(sc);
+	if (error)
+		return error;
+
 	/*
 	 * Check that every dirent we see can also be looked up by hash.
 	 * Userspace usually asks for a 32k buffer, so we will too.
#45 lore raw

PATCH v10 23/27 xfs: scrub extended attributes

In reply to Darrick J. Wong

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

Scrub the hash tree, keys, and values in an extended attribute structure. Refactor the attribute code to use the transaction if the caller supplied one to avoid buffer deadocks.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    3 -
 fs/xfs/scrub/attr.c    |  210 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/common.h  |    2 
 fs/xfs/scrub/scrub.c   |    8 ++
 fs/xfs/scrub/scrub.h   |    2 
 6 files changed, 225 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/attr.c
fs/xfs/Makefile +1 -0
index 69aa88e..4d46399 100644
@@ -148,6 +148,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   trace.o \
 				   agheader.o \
 				   alloc.o \
+				   attr.o \
 				   bmap.o \
 				   btree.o \
 				   common.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
index 8d2bea5..d31d743 100644
@@ -499,9 +499,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_BMBTA	13	/* attr fork block mapping */
 #define XFS_SCRUB_TYPE_BMBTC	14	/* CoW fork block mapping */
 #define XFS_SCRUB_TYPE_DIR	15	/* directory */
+#define XFS_SCRUB_TYPE_XATTR	16	/* extended attribute */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	16
+#define XFS_SCRUB_TYPE_NR	17
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/attr.c new file +210 -0

210 lines not on this page · raw message

fs/xfs/scrub/common.h +2 -0
index 82b8056..f0a33b3 100644
@@ -110,6 +110,8 @@ int xfs_scrub_setup_inode_bmap_data(struct xfs_scrub_context *sc,
 				    struct xfs_inode *ip);
 int xfs_scrub_setup_directory(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
+int xfs_scrub_setup_xattr(struct xfs_scrub_context *sc,
+			  struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/scrub.c +8 -0
index 760492a..046b3e5 100644
@@ -161,6 +161,10 @@ xfs_scrub_teardown(
 			iput(VFS_I(sc->ip));
 		sc->ip = NULL;
 	}
+	if (sc->buf) {
+		kmem_free(sc->buf);
+		sc->buf = NULL;
+	}
 	return error;
 }
 
@@ -234,6 +238,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_directory,
 		.scrub	= xfs_scrub_directory,
 	},
+	{ /* extended attributes */
+		.setup	= xfs_scrub_setup_xattr,
+		.scrub	= xfs_scrub_xattr,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +2 -0
index 4c348e8..b440e26 100644
@@ -54,6 +54,7 @@ struct xfs_scrub_context {
 	const struct xfs_scrub_meta_ops	*ops;
 	struct xfs_trans		*tp;
 	struct xfs_inode		*ip;
+	void				*buf;
 	uint				ilock_flags;
 	bool				try_harder;
 
@@ -78,5 +79,6 @@ int xfs_scrub_bmap_data(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_attr(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);
 int xfs_scrub_directory(struct xfs_scrub_context *sc);
+int xfs_scrub_xattr(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#46 lore raw

PATCH v10 24/27 xfs: scrub symbolic links

In reply to Darrick J. Wong

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

Create the infrastructure to scrub symbolic link data.

---
 fs/xfs/Makefile        |    1 +
 fs/xfs/libxfs/xfs_fs.h |    3 +-
 fs/xfs/scrub/common.h  |    2 +
 fs/xfs/scrub/scrub.c   |    4 ++
 fs/xfs/scrub/scrub.h   |    1 +
 fs/xfs/scrub/symlink.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/symlink.c
fs/xfs/Makefile +1 -0
index 4d46399..28637a6 100644
@@ -159,5 +159,6 @@ xfs-y				+= $(addprefix scrub/, \
 				   refcount.o \
 				   rmap.o \
 				   scrub.o \
+				   symlink.o \
 				   )
 endif
fs/xfs/libxfs/xfs_fs.h +2 -1
index d31d743..955eea7 100644
@@ -500,9 +500,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_BMBTC	14	/* CoW fork block mapping */
 #define XFS_SCRUB_TYPE_DIR	15	/* directory */
 #define XFS_SCRUB_TYPE_XATTR	16	/* extended attribute */
+#define XFS_SCRUB_TYPE_SYMLINK	17	/* symbolic link */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	17
+#define XFS_SCRUB_TYPE_NR	18
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +2 -0
index f0a33b3..eeb6bc4 100644
@@ -112,6 +112,8 @@ int xfs_scrub_setup_directory(struct xfs_scrub_context *sc,
 			      struct xfs_inode *ip);
 int xfs_scrub_setup_xattr(struct xfs_scrub_context *sc,
 			  struct xfs_inode *ip);
+int xfs_scrub_setup_symlink(struct xfs_scrub_context *sc,
+			    struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/scrub.c +4 -0
index 046b3e5..0185a61 100644
@@ -242,6 +242,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_xattr,
 		.scrub	= xfs_scrub_xattr,
 	},
+	{ /* symbolic link */
+		.setup	= xfs_scrub_setup_symlink,
+		.scrub	= xfs_scrub_symlink,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index b440e26..89dfb66 100644
@@ -80,5 +80,6 @@ int xfs_scrub_bmap_attr(struct xfs_scrub_context *sc);
 int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);
 int xfs_scrub_directory(struct xfs_scrub_context *sc);
 int xfs_scrub_xattr(struct xfs_scrub_context *sc);
+int xfs_scrub_symlink(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/symlink.c new file +92 -0

92 lines not on this page · raw message

#47 lore raw

PATCH v10 25/27 xfs: scrub parent pointers

In reply to Darrick J. Wong

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

Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    3 -
 fs/xfs/scrub/common.h  |    2 
 fs/xfs/scrub/parent.c  |  262 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |    4 +
 fs/xfs/scrub/scrub.h   |    1 
 6 files changed, 272 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/parent.c
fs/xfs/Makefile +1 -0
index 28637a6..2193a54 100644
@@ -156,6 +156,7 @@ xfs-y				+= $(addprefix scrub/, \
 				   dir.o \
 				   ialloc.o \
 				   inode.o \
+				   parent.o \
 				   refcount.o \
 				   rmap.o \
 				   scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
index 955eea7..e3d10b4 100644
@@ -501,9 +501,10 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_DIR	15	/* directory */
 #define XFS_SCRUB_TYPE_XATTR	16	/* extended attribute */
 #define XFS_SCRUB_TYPE_SYMLINK	17	/* symbolic link */
+#define XFS_SCRUB_TYPE_PARENT	18	/* parent pointers */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	18
+#define XFS_SCRUB_TYPE_NR	19
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +2 -0
index eeb6bc4..29c3c29 100644
@@ -114,6 +114,8 @@ int xfs_scrub_setup_xattr(struct xfs_scrub_context *sc,
 			  struct xfs_inode *ip);
 int xfs_scrub_setup_symlink(struct xfs_scrub_context *sc,
 			    struct xfs_inode *ip);
+int xfs_scrub_setup_parent(struct xfs_scrub_context *sc,
+			   struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/parent.c new file +262 -0

262 lines not on this page · raw message

fs/xfs/scrub/scrub.c +4 -0
index 0185a61..6dae908 100644
@@ -246,6 +246,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_symlink,
 		.scrub	= xfs_scrub_symlink,
 	},
+	{ /* parent pointers */
+		.setup	= xfs_scrub_setup_parent,
+		.scrub	= xfs_scrub_parent,
+	},
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index 89dfb66..3363df9 100644
@@ -81,5 +81,6 @@ int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);
 int xfs_scrub_directory(struct xfs_scrub_context *sc);
 int xfs_scrub_xattr(struct xfs_scrub_context *sc);
 int xfs_scrub_symlink(struct xfs_scrub_context *sc);
+int xfs_scrub_parent(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#48 lore raw

PATCH v10 26/27 xfs: scrub realtime bitmap/summary

In reply to Darrick J. Wong

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

Perform simple tests of the realtime bitmap and summary.

---
 fs/xfs/Makefile            |    2 +
 fs/xfs/libxfs/xfs_format.h |    5 ++
 fs/xfs/libxfs/xfs_fs.h     |    4 +-
 fs/xfs/scrub/common.h      |    1 
 fs/xfs/scrub/rtbitmap.c    |   98 ++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c       |   15 +++++++
 fs/xfs/scrub/scrub.h       |    2 +
 7 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/rtbitmap.c
fs/xfs/Makefile +2 -0
index 2193a54..9ce581e 100644
@@ -162,4 +162,6 @@ xfs-y				+= $(addprefix scrub/, \
 				   scrub.o \
 				   symlink.o \
 				   )
+
+xfs-$(CONFIG_XFS_RT)		+= scrub/rtbitmap.o
 endif
fs/xfs/libxfs/xfs_format.h +5 -0
index 154c3dd..d4d9bef 100644
@@ -315,6 +315,11 @@ static inline bool xfs_sb_good_version(struct xfs_sb *sbp)
 	return false;
 }
 
+static inline bool xfs_sb_version_hasrealtime(struct xfs_sb *sbp)
+{
+	return sbp->sb_rblocks > 0;
+}
+
 /*
  * Detect a mismatched features2 field.  Older kernels read/wrote
  * this into the wrong slot, so to be safe we keep them in sync.
fs/xfs/libxfs/xfs_fs.h +3 -1
index e3d10b4..3f37156 100644
@@ -502,9 +502,11 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_XATTR	16	/* extended attribute */
 #define XFS_SCRUB_TYPE_SYMLINK	17	/* symbolic link */
 #define XFS_SCRUB_TYPE_PARENT	18	/* parent pointers */
+#define XFS_SCRUB_TYPE_RTBITMAP	19	/* realtime bitmap */
+#define XFS_SCRUB_TYPE_RTSUM	20	/* realtime summary */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	19
+#define XFS_SCRUB_TYPE_NR	21
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +1 -0
index 29c3c29..871204a 100644
@@ -116,6 +116,7 @@ int xfs_scrub_setup_symlink(struct xfs_scrub_context *sc,
 			    struct xfs_inode *ip);
 int xfs_scrub_setup_parent(struct xfs_scrub_context *sc,
 			   struct xfs_inode *ip);
+int xfs_scrub_setup_rt(struct xfs_scrub_context *sc, struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/rtbitmap.c new file +98 -0

98 lines not on this page · raw message

fs/xfs/scrub/scrub.c +15 -0
index 6dae908..23b010d 100644
@@ -250,6 +250,21 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 		.setup	= xfs_scrub_setup_parent,
 		.scrub	= xfs_scrub_parent,
 	},
+#ifdef CONFIG_XFS_RT
+	{ /* realtime bitmap */
+		.setup	= xfs_scrub_setup_rt,
+		.scrub	= xfs_scrub_rtbitmap,
+		.has	= xfs_sb_version_hasrealtime,
+	},
+	{ /* realtime summary */
+		.setup	= xfs_scrub_setup_rt,
+		.scrub	= xfs_scrub_rtsummary,
+		.has	= xfs_sb_version_hasrealtime,
+	},
+#else
+	{ NULL },
+	{ NULL },
+#endif
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +2 -0
index 3363df9..97fd03f 100644
@@ -82,5 +82,7 @@ int xfs_scrub_directory(struct xfs_scrub_context *sc);
 int xfs_scrub_xattr(struct xfs_scrub_context *sc);
 int xfs_scrub_symlink(struct xfs_scrub_context *sc);
 int xfs_scrub_parent(struct xfs_scrub_context *sc);
+int xfs_scrub_rtbitmap(struct xfs_scrub_context *sc);
+int xfs_scrub_rtsummary(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#49 lore raw

PATCH v10 27/27 xfs: scrub quota information

In reply to Darrick J. Wong

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

Perform some quick sanity testing of the disk quota information.

---
 fs/xfs/Makefile        |    1 
 fs/xfs/libxfs/xfs_fs.h |    5 +
 fs/xfs/scrub/common.h  |    1 
 fs/xfs/scrub/quota.c   |  258 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/scrub/scrub.c   |   18 +++
 fs/xfs/scrub/scrub.h   |    1 
 6 files changed, 283 insertions(+), 1 deletion(-)
 create mode 100644 fs/xfs/scrub/quota.c
fs/xfs/Makefile +1 -0
index 9ce581e..3152469 100644
@@ -164,4 +164,5 @@ xfs-y				+= $(addprefix scrub/, \
 				   )
 
 xfs-$(CONFIG_XFS_RT)		+= scrub/rtbitmap.o
+xfs-$(CONFIG_XFS_QUOTA)		+= scrub/quota.o
 endif
fs/xfs/libxfs/xfs_fs.h +4 -1
index 3f37156..fba0105 100644
@@ -504,9 +504,12 @@ struct xfs_scrub_metadata {
 #define XFS_SCRUB_TYPE_PARENT	18	/* parent pointers */
 #define XFS_SCRUB_TYPE_RTBITMAP	19	/* realtime bitmap */
 #define XFS_SCRUB_TYPE_RTSUM	20	/* realtime summary */
+#define XFS_SCRUB_TYPE_UQUOTA	21	/* user quotas */
+#define XFS_SCRUB_TYPE_GQUOTA	22	/* group quotas */
+#define XFS_SCRUB_TYPE_PQUOTA	23	/* project quotas */
 
 /* Number of scrub subcommands. */
-#define XFS_SCRUB_TYPE_NR	21
+#define XFS_SCRUB_TYPE_NR	24
 
 /* i: Repair this metadata. */
 #define XFS_SCRUB_IFLAG_REPAIR		(1 << 0)
fs/xfs/scrub/common.h +1 -0
index 871204a..a4a2ca5 100644
@@ -117,6 +117,7 @@ int xfs_scrub_setup_symlink(struct xfs_scrub_context *sc,
 int xfs_scrub_setup_parent(struct xfs_scrub_context *sc,
 			   struct xfs_inode *ip);
 int xfs_scrub_setup_rt(struct xfs_scrub_context *sc, struct xfs_inode *ip);
+int xfs_scrub_setup_quota(struct xfs_scrub_context *sc, struct xfs_inode *ip);
 
 void xfs_scrub_ag_free(struct xfs_scrub_context *sc, struct xfs_scrub_ag *sa);
 int xfs_scrub_ag_init(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
fs/xfs/scrub/quota.c new file +258 -0

258 lines not on this page · raw message

fs/xfs/scrub/scrub.c +18 -0
index 23b010d..8cddfcd 100644
@@ -265,6 +265,24 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = {
 	{ NULL },
 	{ NULL },
 #endif
+#ifdef CONFIG_XFS_QUOTA
+	{ /* user quota */
+		.setup = xfs_scrub_setup_quota,
+		.scrub = xfs_scrub_quota,
+	},
+	{ /* group quota */
+		.setup = xfs_scrub_setup_quota,
+		.scrub = xfs_scrub_quota,
+	},
+	{ /* project quota */
+		.setup = xfs_scrub_setup_quota,
+		.scrub = xfs_scrub_quota,
+	},
+#else
+	{ NULL },
+	{ NULL },
+	{ NULL },
+#endif
 };
 
 /* Dispatch metadata scrubbing. */
fs/xfs/scrub/scrub.h +1 -0
index 97fd03f..3218664 100644
@@ -84,5 +84,6 @@ int xfs_scrub_symlink(struct xfs_scrub_context *sc);
 int xfs_scrub_parent(struct xfs_scrub_context *sc);
 int xfs_scrub_rtbitmap(struct xfs_scrub_context *sc);
 int xfs_scrub_rtsummary(struct xfs_scrub_context *sc);
+int xfs_scrub_quota(struct xfs_scrub_context *sc);
 
 #endif	/* __XFS_SCRUB_SCRUB_H__ */
#50 lore raw

PATCH 1/1 man: describe the metadata scrubbing ioctl

In reply to Darrick J. Wong

Document the XFS-specific metadata scrub/repair ioctl's behavior, arguments, and side effects. Dump this in xfsprogs for lack of a better destination.

---
 man/Makefile                        |    2 
 man/man2/ioctl_xfs_scrub_metadata.2 |  298 +++++++++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 man/man2/ioctl_xfs_scrub_metadata.2
man/Makefile +1 -1
index 863284c..cae891f 100644
@@ -5,7 +5,7 @@
 TOPDIR = ..
 include $(TOPDIR)/include/builddefs
 
-SUBDIRS = man3 man5 man8
+SUBDIRS = man2 man3 man5 man8
 
 default : $(SUBDIRS)
 
man/man2/ioctl_xfs_scrub_metadata.2 new file +298 -0

298 lines not on this page · raw message