@@ -19,6 +19,7 @@ XFS_SCRUB_FAIL_PROG = xfs_scrub_fail
XFS_SCRUB_ARGS = -p
XFS_SCRUB_SERVICE_ARGS = -b
XFS_SCRUBBED_PROG = xfs_scrubbed
+XFS_SCRUBBED_HELPER = xfs_scrubbed_start
ifeq ($(HAVE_SYSTEMD),yes)
INSTALL_SCRUB += install-systemd
SYSTEMD_SERVICES=\
@@ -29,8 +30,9 @@ SYSTEMD_SERVICES=\
xfs_scrub_all.service \
xfs_scrub_all_fail.service \
xfs_scrub_all.timer \
- system-xfs_scrub.slice
-OPTIONAL_TARGETS += $(SYSTEMD_SERVICES)
+ system-xfs_scrub.slice \
+ xfs_scrubbed@.service
+OPTIONAL_TARGETS += $(SYSTEMD_SERVICES) $(XFS_SCRUBBED_HELPER)
endif
ifeq ($(HAVE_CROND),yes)
INSTALL_SCRUB += install-crond
@@ -181,7 +183,7 @@ install-systemd: default $(SYSTEMD_SERVICES)
$(INSTALL) -m 755 -d $(SYSTEMD_SYSTEM_UNIT_DIR)
$(INSTALL) -m 644 $(SYSTEMD_SERVICES) $(SYSTEMD_SYSTEM_UNIT_DIR)
$(INSTALL) -m 755 -d $(PKG_LIBEXEC_DIR)
- $(INSTALL) -m 755 $(XFS_SCRUB_FAIL_PROG) $(PKG_LIBEXEC_DIR)
+ $(INSTALL) -m 755 $(XFS_SCRUB_FAIL_PROG) $(XFS_SCRUBBED_HELPER) $(PKG_LIBEXEC_DIR)
install-crond: default $(CRONTABS)
$(INSTALL) -m 755 -d $(CROND_DIR)
@@ -11,3 +11,6 @@
# supplying UDISKS_AUTO=0 here changes the HintAuto property of the block
# device abstraction to mean "do not automatically start" (e.g. mount).
SUBSYSTEM=="block", ENV{ID_FS_TYPE}=="xfs|xfs_external_log", ENV{UDISKS_AUTO}="0"
+
+# Start the background scrubber automatically
+ACTION=="add", SUBSYSTEM=="xfs", ENV{TYPE}=="mount", RUN+="xfs_scrubbed_start"
@@ -17,6 +17,7 @@ import datetime
import errno
import ctypes
import ctypes.util
+import time
debug = False
log = False
@@ -505,6 +506,13 @@ def main():
ret = monitor(args.mountpoint)
except KeyboardInterrupt:
ret = 0
+
+ # See the service mode comments in xfs_scrub.c for why we do this.
+ if 'SERVICE_MODE' in os.environ:
+ time.sleep(2)
+ if ret != 0:
+ ret = 1
+
sys.exit(ret)
if __name__ == '__main__':
new file mode 100644
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+[Unit]
+Description=Self Healing of XFS Metadata for %f
+Documentation=man:xfs_scrubbed(8)
+
+# Explicitly require the capabilities that this program needs
+ConditionCapability=CAP_SYS_ADMIN
+
+# Must be a mountpoint
+ConditionPathIsMountPoint=%f
+RequiresMountsFor=%f
+
+[Service]
+Type=oneshot
+Environment=SERVICE_MODE=1
+ExecStart=@pkg_libexec_dir@/xfs_scrubbed --repair --log %f
+SyslogIdentifier=%N
+
+# Run scrub with minimal CPU and IO priority so that nothing else will starve.
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+CPUAccounting=true
+Nice=19
+
+# Create the service underneath the scrub background service slice so that we
+# can control resource usage.
+Slice=system-xfs_scrub.slice
+
+# No realtime CPU scheduling
+RestrictRealtime=true
+
+# Dynamically create a user that isn't root
+DynamicUser=true
+
+# Make the entire filesystem readonly, but don't hide /home and don't use a
+# private bind mount like xfs_scrub. We don't want to pin the filesystem,
+# because we want umount to work correctly and this service to stop
+# automatically.
+ProtectSystem=strict
+ProtectHome=no
+PrivateTmp=true
+PrivateDevices=true
+
+# Don't let scrub complain about paths in /etc/projects that have been hidden
+# by our sandboxing. scrub doesn't care about project ids anyway.
+InaccessiblePaths=-/etc/projects
+
+# No network access
+PrivateNetwork=true
+ProtectHostname=true
+RestrictAddressFamilies=none
+IPAddressDeny=any
+
+# Don't let the program mess with the kernel configuration at all
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+ProtectProc=invisible
+RestrictNamespaces=true
+
+# Hide everything in /proc, even /proc/mounts
+ProcSubset=pid
+
+# Only allow the default personality Linux
+LockPersonality=true
+
+# No writable memory pages
+MemoryDenyWriteExecute=true
+
+# Don't let our mounts leak out to the host
+PrivateMounts=true
+
+# Restrict system calls to the native arch and only enough to get things going
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged
+SystemCallFilter=~@resources
+SystemCallFilter=~@mount
+
+# xfs_scrub needs these privileges to run, and no others
+CapabilityBoundingSet=CAP_SYS_ADMIN
+AmbientCapabilities=CAP_SYS_ADMIN
+NoNewPrivileges=true
+
+# xfs_scrubbed doesn't create files
+UMask=7777
+
+# No access to hardware /dev files except for block devices
+ProtectClock=true
+DevicePolicy=closed
new file mode 100755
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2024 Oracle. All Rights Reserved.
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+# Start the xfs_scrubbed service when the filesystem is mounted
+
+command -v systemctl || exit 0
+
+grep "^$SOURCE[[:space:]]" /proc/mounts | while read source mntpt therest; do
+ inst="$(systemd-escape --path "$mntpt")"
+ systemctl restart --no-block "xfs_scrubbed@$inst" && break
+done
+
+exit 0