From 774eadda46a487d67f3d3b8c13df32d4cdd27d4a Mon Sep 17 00:00:00 2001 From: John Titor <50095635+JohnRTitor@users.noreply.github.com> Date: Tue, 10 Jun 2025 21:12:58 +0530 Subject: [PATCH] nixos/bcachefs: init module for autoScrub Signed-off-by: John Titor <50095635+JohnRTitor@users.noreply.github.com> --- nixos/modules/tasks/filesystems/bcachefs.nix | 116 +++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/nixos/modules/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix index a3d0e9463832..ddd7a25019d1 100644 --- a/nixos/modules/tasks/filesystems/bcachefs.nix +++ b/nixos/modules/tasks/filesystems/bcachefs.nix @@ -7,6 +7,7 @@ }: let + cfgScrub = config.services.bcachefs.autoScrub; bootFs = lib.filterAttrs ( n: fs: (fs.fsType == "bcachefs") && (utils.fsNeededForBoot fs) @@ -159,6 +160,32 @@ let in { + options.services.bcachefs.autoScrub = { + enable = lib.mkEnableOption "regular bcachefs scrub"; + + fileSystems = lib.mkOption { + type = lib.types.listOf lib.types.path; + example = [ "/" ]; + description = '' + List of paths to bcachefs filesystems to regularly call {command}`bcachefs scrub` on. + Defaults to all mount points with bcachefs filesystems. + ''; + }; + + interval = lib.mkOption { + default = "monthly"; + type = lib.types.str; + example = "weekly"; + description = '' + Systemd calendar expression for when to scrub bcachefs filesystems. + The recommended period is a month but could be less. + See + {manpage}`systemd.time(7)` + for more information on the syntax. + ''; + }; + }; + config = lib.mkIf (config.boot.supportedFilesystems.bcachefs or false) ( lib.mkMerge [ { @@ -205,6 +232,95 @@ in boot.initrd.systemd.services = lib.mapAttrs' (mkUnits "/sysroot") bootFs; }) + + (lib.mkIf (cfgScrub.enable) { + assertions = [ + { + assertion = lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.14"; + message = "Bcachefs scrubbing is supported from kernel version 6.14 or later."; + } + { + assertion = cfgScrub.enable -> (cfgScrub.fileSystems != [ ]); + message = '' + If 'services.bcachefs.autoScrub' is enabled, you need to have at least one + bcachefs file system mounted via 'fileSystems' or specify a list manually + in 'services.bcachefs.autoScrub.fileSystems'. + ''; + } + ]; + + # This will remove duplicated units from either having a filesystem mounted multiple + # time, or additionally mounted subvolumes, as well as having a filesystem span + # multiple devices (provided the same device is used to mount said filesystem). + services.bcachefs.autoScrub.fileSystems = + let + isDeviceInList = list: device: builtins.filter (e: e.device == device) list != [ ]; + + uniqueDeviceList = lib.foldl' ( + acc: e: if isDeviceInList acc e.device then acc else acc ++ [ e ] + ) [ ]; + in + lib.mkDefault ( + map (e: e.mountPoint) ( + uniqueDeviceList ( + lib.mapAttrsToList (name: fs: { + mountPoint = fs.mountPoint; + device = fs.device; + }) (lib.filterAttrs (name: fs: fs.fsType == "bcachefs") config.fileSystems) + ) + ) + ); + + systemd.timers = + let + scrubTimer = + fs: + let + fs' = utils.escapeSystemdPath fs; + in + lib.nameValuePair "bcachefs-scrub-${fs'}" { + description = "regular bcachefs scrub timer on ${fs}"; + + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfgScrub.interval; + AccuracySec = "1d"; + Persistent = true; + }; + }; + in + lib.listToAttrs (map scrubTimer cfgScrub.fileSystems); + + systemd.services = + let + scrubService = + fs: + let + fs' = utils.escapeSystemdPath fs; + in + lib.nameValuePair "bcachefs-scrub-${fs'}" { + description = "bcachefs scrub on ${fs}"; + # scrub prevents suspend2ram or proper shutdown + conflicts = [ + "shutdown.target" + "sleep.target" + ]; + before = [ + "shutdown.target" + "sleep.target" + ]; + + script = "${lib.getExe pkgs.bcachefs-tools} data scrub ${fs}"; + + serviceConfig = { + Type = "oneshot"; + Nice = 19; + IOSchedulingClass = "idle"; + }; + }; + in + lib.listToAttrs (map scrubService cfgScrub.fileSystems); + }) ] ); }