at 18.09-beta 4.5 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4 5let 6 7 inInitrd = any (fs: fs == "btrfs") config.boot.initrd.supportedFilesystems; 8 inSystem = any (fs: fs == "btrfs") config.boot.supportedFilesystems; 9 10 cfgScrub = config.services.btrfs.autoScrub; 11 12 enableAutoScrub = cfgScrub.enable; 13 enableBtrfs = inInitrd || inSystem || enableAutoScrub; 14 15in 16 17{ 18 options = { 19 # One could also do regular btrfs balances, but that shouldn't be necessary 20 # during normal usage and as long as the filesystems aren't filled near capacity 21 services.btrfs.autoScrub = { 22 enable = mkEnableOption "Enable regular btrfs scrub"; 23 24 fileSystems = mkOption { 25 type = types.listOf types.path; 26 example = [ "/" ]; 27 description = '' 28 List of paths to btrfs filesystems to regularily call <command>btrfs scrub</command> on. 29 Defaults to all mount points with btrfs filesystems. 30 If you mount a filesystem multiple times or additionally mount subvolumes, 31 you need to manually specify this list to avoid scrubbing multiple times. 32 ''; 33 }; 34 35 interval = mkOption { 36 default = "monthly"; 37 type = types.str; 38 example = "weekly"; 39 description = '' 40 Systemd calendar expression for when to scrub btrfs filesystems. 41 The recommended period is a month but could be less 42 (<citerefentry><refentrytitle>btrfs-scrub</refentrytitle> 43 <manvolnum>8</manvolnum></citerefentry>). 44 See 45 <citerefentry><refentrytitle>systemd.time</refentrytitle> 46 <manvolnum>7</manvolnum></citerefentry> 47 for more information on the syntax. 48 ''; 49 }; 50 51 }; 52 }; 53 54 config = mkMerge [ 55 (mkIf enableBtrfs { 56 system.fsPackages = [ pkgs.btrfs-progs ]; 57 58 boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ]; 59 60 boot.initrd.extraUtilsCommands = mkIf inInitrd 61 '' 62 copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs 63 ln -sv btrfs $out/bin/btrfsck 64 ln -sv btrfsck $out/bin/fsck.btrfs 65 ''; 66 67 boot.initrd.extraUtilsCommandsTest = mkIf inInitrd 68 '' 69 $out/bin/btrfs --version 70 ''; 71 72 boot.initrd.postDeviceCommands = mkIf inInitrd 73 '' 74 btrfs device scan 75 ''; 76 }) 77 78 (mkIf enableAutoScrub { 79 assertions = [ 80 { 81 assertion = cfgScrub.enable -> (cfgScrub.fileSystems != []); 82 message = '' 83 If 'services.btrfs.autoScrub' is enabled, you need to have at least one 84 btrfs file system mounted via 'fileSystems' or specify a list manually 85 in 'services.btrfs.autoScrub.fileSystems'. 86 ''; 87 } 88 ]; 89 90 # This will yield duplicated units if the user mounts a filesystem multiple times 91 # or additionally mounts subvolumes, but going the other way around via devices would 92 # yield duplicated units when a filesystem spans multiple devices. 93 # This way around seems like the more sensible default. 94 services.btrfs.autoScrub.fileSystems = mkDefault (mapAttrsToList (name: fs: fs.mountPoint) 95 (filterAttrs (name: fs: fs.fsType == "btrfs") config.fileSystems)); 96 97 # TODO: Did not manage to do it via the usual btrfs-scrub@.timer/.service 98 # template units due to problems enabling the parameterized units, 99 # so settled with many units and templating via nix for now. 100 # https://github.com/NixOS/nixpkgs/pull/32496#discussion_r156527544 101 systemd.timers = let 102 scrubTimer = fs: let 103 fs' = utils.escapeSystemdPath fs; 104 in nameValuePair "btrfs-scrub-${fs'}" { 105 description = "regular btrfs scrub timer on ${fs}"; 106 107 wantedBy = [ "timers.target" ]; 108 timerConfig = { 109 OnCalendar = cfgScrub.interval; 110 AccuracySec = "1d"; 111 Persistent = true; 112 }; 113 }; 114 in listToAttrs (map scrubTimer cfgScrub.fileSystems); 115 116 systemd.services = let 117 scrubService = fs: let 118 fs' = utils.escapeSystemdPath fs; 119 in nameValuePair "btrfs-scrub-${fs'}" { 120 description = "btrfs scrub on ${fs}"; 121 122 serviceConfig = { 123 Type = "oneshot"; 124 Nice = 19; 125 IOSchedulingClass = "idle"; 126 ExecStart = "${pkgs.btrfs-progs}/bin/btrfs scrub start -B ${fs}"; 127 }; 128 }; 129 in listToAttrs (map scrubService cfgScrub.fileSystems); 130 }) 131 ]; 132}