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-09-17 18:55:29 -0700
commit309c2b4fe66b1402154c1c22af53bed7b0ea4321 (patch)
tree6fd2e162c4e02d3a12542ba7780131491d3873f3
parent39d240cdb7c034a8dcec297435c1363858bd17d9 (diff)
xfs: allow userspace to rebuild metadata structuresdefrag-freespace_2021-09-17
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/xfs_ioctl.c3
4 files changed, 14 insertions, 3 deletions
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 7ac2b0a99aae..5911508d8f38 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -791,9 +791,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 70a42897cd22..dc7b2f41c1d2 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -194,7 +194,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 d3944a1d0146..3a43fe95dfc8 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -476,6 +476,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/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;