summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:25:40 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-10-22 16:41:15 -0700
commit5ada217abe8fe47761c91a128f1ed721b0c2a43b (patch)
treeef386d49e3ade36f7dd1f963a4e7137badba9974
parentb963e3a477e67d48ebe82463af6d601dd5c2ce45 (diff)
xfs: allow userspace to rebuild metadata structuresdefrag-freespace_2021-10-22
Add a new (superuser-only) flag to the online metadata repair ioctl to force it to rebuild structures, even if they're not broken. We will use this to move metadata structures out of the way during a free space defragmentation operation. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/libxfs/xfs_fs.h6
-rw-r--r--fs/xfs/scrub/common.h3
-rw-r--r--fs/xfs/scrub/scrub.c5
-rw-r--r--fs/xfs/scrub/trace.h3
-rw-r--r--fs/xfs/xfs_ioctl.c3
5 files changed, 16 insertions, 4 deletions
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index c0a42019f8f5..f30767902660 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -794,9 +794,13 @@ struct xfs_scrub_metadata {
/* i: Don't mark inodes DONTCACHE at the end. */
#define XFS_SCRUB_IFLAG_RETAIN_INODES (1 << 9)
+/* i: Rebuild the data structure. */
+#define XFS_SCRUB_IFLAG_FORCE_REBUILD (1 << 10)
+
#define XFS_SCRUB_FLAGS_IN (XFS_SCRUB_IFLAG_REPAIR | \
XFS_SCRUB_IFLAG_FREEZE_OK | \
- XFS_SCRUB_IFLAG_RETAIN_INODES)
+ XFS_SCRUB_IFLAG_RETAIN_INODES | \
+ XFS_SCRUB_IFLAG_FORCE_REBUILD)
#define XFS_SCRUB_FLAGS_OUT (XFS_SCRUB_OFLAG_CORRUPT | \
XFS_SCRUB_OFLAG_PREEN | \
XFS_SCRUB_OFLAG_XFAIL | \
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index 819bb7e2007a..81361d67cefd 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -202,7 +202,8 @@ static inline bool xfs_scrub_needs_repair(struct xfs_scrub_metadata *sm)
{
return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
XFS_SCRUB_OFLAG_XCORRUPT |
- XFS_SCRUB_OFLAG_PREEN);
+ XFS_SCRUB_OFLAG_PREEN |
+ XFS_SCRUB_IFLAG_FORCE_REBUILD);
}
int xchk_iwalk_find_next(struct xfs_mount *mp, struct xfs_trans *tp,
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index 181d66773b45..875cf7ac08d0 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -464,6 +464,11 @@ xchk_validate_inputs(
goto out;
}
+ /* No rebuild without repair. */
+ if ((sm->sm_flags & XFS_SCRUB_IFLAG_FORCE_REBUILD) &&
+ !(sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
+ return -EINVAL;
+
/*
* We only want to repair read-write v5+ filesystems. Defer the check
* for ops->repair until after our scrub confirms that we need to
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 5c43578a174e..a2e6094e8c5a 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -111,7 +111,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER);
{ XFS_SCRUB_OFLAG_WARNING, "warning" }, \
{ XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED, "norepair" }, \
{ XFS_SCRUB_IFLAG_FREEZE_OK, "freeze" }, \
- { XFS_SCRUB_IFLAG_RETAIN_INODES, "icache" }
+ { XFS_SCRUB_IFLAG_RETAIN_INODES, "icache" }, \
+ { XFS_SCRUB_IFLAG_FORCE_REBUILD, "rebuild" }
DECLARE_EVENT_CLASS(xchk_class,
TP_PROTO(struct xfs_inode *ip, struct xfs_scrub_metadata *sm,
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 8913c27ddd11..525eba22c2a2 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1810,7 +1810,8 @@ xfs_ioc_scrub_metadata(
if (copy_from_user(&scrub, arg, sizeof(scrub)))
return -EFAULT;
- if ((scrub.sm_flags & XFS_SCRUB_IFLAG_FREEZE_OK) &&
+ if ((scrub.sm_flags & (XFS_SCRUB_IFLAG_FREEZE_OK |
+ XFS_SCRUB_IFLAG_FORCE_REBUILD)) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;