aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix')
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix132
1 files changed, 132 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix
new file mode 100644
index 00000000000..48be18c7102
--- /dev/null
+++ b/nixpkgs/nixos/modules/tasks/filesystems/btrfs.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+
+let
+
+ inInitrd = any (fs: fs == "btrfs") config.boot.initrd.supportedFilesystems;
+ inSystem = any (fs: fs == "btrfs") config.boot.supportedFilesystems;
+
+ cfgScrub = config.services.btrfs.autoScrub;
+
+ enableAutoScrub = cfgScrub.enable;
+ enableBtrfs = inInitrd || inSystem || enableAutoScrub;
+
+in
+
+{
+ options = {
+ # One could also do regular btrfs balances, but that shouldn't be necessary
+ # during normal usage and as long as the filesystems aren't filled near capacity
+ services.btrfs.autoScrub = {
+ enable = mkEnableOption "regular btrfs scrub";
+
+ fileSystems = mkOption {
+ type = types.listOf types.path;
+ example = [ "/" ];
+ description = ''
+ List of paths to btrfs filesystems to regularily call <command>btrfs scrub</command> on.
+ Defaults to all mount points with btrfs filesystems.
+ If you mount a filesystem multiple times or additionally mount subvolumes,
+ you need to manually specify this list to avoid scrubbing multiple times.
+ '';
+ };
+
+ interval = mkOption {
+ default = "monthly";
+ type = types.str;
+ example = "weekly";
+ description = ''
+ Systemd calendar expression for when to scrub btrfs filesystems.
+ The recommended period is a month but could be less
+ (<citerefentry><refentrytitle>btrfs-scrub</refentrytitle>
+ <manvolnum>8</manvolnum></citerefentry>).
+ See
+ <citerefentry><refentrytitle>systemd.time</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry>
+ for more information on the syntax.
+ '';
+ };
+
+ };
+ };
+
+ config = mkMerge [
+ (mkIf enableBtrfs {
+ system.fsPackages = [ pkgs.btrfs-progs ];
+
+ boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+
+ boot.initrd.extraUtilsCommands = mkIf inInitrd
+ ''
+ copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs
+ ln -sv btrfs $out/bin/btrfsck
+ ln -sv btrfsck $out/bin/fsck.btrfs
+ '';
+
+ boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
+ ''
+ $out/bin/btrfs --version
+ '';
+
+ boot.initrd.postDeviceCommands = mkIf inInitrd
+ ''
+ btrfs device scan
+ '';
+ })
+
+ (mkIf enableAutoScrub {
+ assertions = [
+ {
+ assertion = cfgScrub.enable -> (cfgScrub.fileSystems != []);
+ message = ''
+ If 'services.btrfs.autoScrub' is enabled, you need to have at least one
+ btrfs file system mounted via 'fileSystems' or specify a list manually
+ in 'services.btrfs.autoScrub.fileSystems'.
+ '';
+ }
+ ];
+
+ # This will yield duplicated units if the user mounts a filesystem multiple times
+ # or additionally mounts subvolumes, but going the other way around via devices would
+ # yield duplicated units when a filesystem spans multiple devices.
+ # This way around seems like the more sensible default.
+ services.btrfs.autoScrub.fileSystems = mkDefault (mapAttrsToList (name: fs: fs.mountPoint)
+ (filterAttrs (name: fs: fs.fsType == "btrfs") config.fileSystems));
+
+ # TODO: Did not manage to do it via the usual btrfs-scrub@.timer/.service
+ # template units due to problems enabling the parameterized units,
+ # so settled with many units and templating via nix for now.
+ # https://github.com/NixOS/nixpkgs/pull/32496#discussion_r156527544
+ systemd.timers = let
+ scrubTimer = fs: let
+ fs' = utils.escapeSystemdPath fs;
+ in nameValuePair "btrfs-scrub-${fs'}" {
+ description = "regular btrfs scrub timer on ${fs}";
+
+ wantedBy = [ "timers.target" ];
+ timerConfig = {
+ OnCalendar = cfgScrub.interval;
+ AccuracySec = "1d";
+ Persistent = true;
+ };
+ };
+ in listToAttrs (map scrubTimer cfgScrub.fileSystems);
+
+ systemd.services = let
+ scrubService = fs: let
+ fs' = utils.escapeSystemdPath fs;
+ in nameValuePair "btrfs-scrub-${fs'}" {
+ description = "btrfs scrub on ${fs}";
+
+ serviceConfig = {
+ Type = "oneshot";
+ Nice = 19;
+ IOSchedulingClass = "idle";
+ ExecStart = "${pkgs.btrfs-progs}/bin/btrfs scrub start -B ${fs}";
+ };
+ };
+ in listToAttrs (map scrubService cfgScrub.fileSystems);
+ })
+ ];
+}