about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/config/zram.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/config/zram.nix')
-rw-r--r--nixpkgs/nixos/modules/config/zram.nix196
1 files changed, 67 insertions, 129 deletions
diff --git a/nixpkgs/nixos/modules/config/zram.nix b/nixpkgs/nixos/modules/config/zram.nix
index 34e80df47a40..991387ea9b2b 100644
--- a/nixpkgs/nixos/modules/config/zram.nix
+++ b/nixpkgs/nixos/modules/config/zram.nix
@@ -1,45 +1,27 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.zramSwap;
-
-  # don't set swapDevices as mkDefault, so we can detect user had read our warning
-  # (see below) and made an action (or not)
-  devicesCount = if cfg.swapDevices != null then cfg.swapDevices else cfg.numDevices;
-
-  devices = map (nr: "zram${toString nr}") (range 0 (devicesCount - 1));
-
-  modprobe = "${pkgs.kmod}/bin/modprobe";
-
-  warnings =
-  assert cfg.swapDevices != null -> cfg.numDevices >= cfg.swapDevices;
-  flatten [
-    (optional (cfg.numDevices > 1 && cfg.swapDevices == null) ''
-      Using several small zram devices as swap is no better than using one large.
-      Set either zramSwap.numDevices = 1 or explicitly set zramSwap.swapDevices.
-
-      Previously multiple zram devices were used to enable multithreaded
-      compression. Linux supports multithreaded compression for 1 device
-      since 3.15. See https://lkml.org/lkml/2014/2/28/404 for details.
-    '')
-  ];
+  devices = map (nr: "zram${toString nr}") (lib.range 0 (cfg.swapDevices - 1));
 
 in
 
 {
 
+  imports = [
+    (lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
+  ];
+
   ###### interface
 
   options = {
 
     zramSwap = {
 
-      enable = mkOption {
+      enable = lib.mkOption {
         default = false;
-        type = types.bool;
+        type = lib.types.bool;
         description = lib.mdDoc ''
           Enable in-memory compressed devices and swap space provided by the zram
           kernel module.
@@ -49,49 +31,38 @@ in
         '';
       };
 
-      numDevices = mkOption {
+      swapDevices = lib.mkOption {
         default = 1;
-        type = types.int;
-        description = lib.mdDoc ''
-          Number of zram devices to create. See also
-          `zramSwap.swapDevices`
-        '';
-      };
-
-      swapDevices = mkOption {
-        default = null;
-        example = 1;
-        type = with types; nullOr int;
+        type = lib.types.int;
         description = lib.mdDoc ''
-          Number of zram devices to be used as swap. Must be
-          `<= zramSwap.numDevices`.
-          Default is same as `zramSwap.numDevices`, recommended is 1.
+          Number of zram devices to be used as swap, recommended is 1.
         '';
       };
 
-      memoryPercent = mkOption {
+      memoryPercent = lib.mkOption {
         default = 50;
-        type = types.int;
+        type = lib.types.int;
         description = lib.mdDoc ''
-          Maximum amount of memory that can be used by the zram swap devices
+          Maximum total amount of memory that can be stored in the zram swap devices
           (as a percentage of your total memory). Defaults to 1/2 of your total
-          RAM. Run `zramctl` to check how good memory is
-          compressed.
+          RAM. Run `zramctl` to check how good memory is compressed.
+          This doesn't define how much memory will be used by the zram swap devices.
         '';
       };
 
-      memoryMax = mkOption {
+      memoryMax = lib.mkOption {
         default = null;
-        type = with types; nullOr int;
+        type = with lib.types; nullOr int;
         description = lib.mdDoc ''
-          Maximum total amount of memory (in bytes) that can be used by the zram
+          Maximum total amount of memory (in bytes) that can be stored in the zram
           swap devices.
+          This doesn't define how much memory will be used by the zram swap devices.
         '';
       };
 
-      priority = mkOption {
+      priority = lib.mkOption {
         default = 5;
-        type = types.int;
+        type = lib.types.int;
         description = lib.mdDoc ''
           Priority of the zram swap devices. It should be a number higher than
           the priority of your disk-based swap devices (so that the system will
@@ -99,25 +70,41 @@ in
         '';
       };
 
-      algorithm = mkOption {
+      algorithm = lib.mkOption {
         default = "zstd";
         example = "lz4";
-        type = with types; either (enum [ "lzo" "lz4" "zstd" ]) str;
-        description = ''
-          Compression algorithm. <literal>lzo</literal> has good compression,
-          but is slow. <literal>lz4</literal> has bad compression, but is fast.
-          <literal>zstd</literal> is both good compression and fast, but requires newer kernel.
+        type = with lib.types; either (enum [ "lzo" "lz4" "zstd" ]) str;
+        description = lib.mdDoc ''
+          Compression algorithm. `lzo` has good compression,
+          but is slow. `lz4` has bad compression, but is fast.
+          `zstd` is both good compression and fast, but requires newer kernel.
           You can check what other algorithms are supported by your zram device with
-          <programlisting>cat /sys/class/block/zram*/comp_algorithm</programlisting>
+          {command}`cat /sys/class/block/zram*/comp_algorithm`
+        '';
+      };
+
+      writebackDevice = lib.mkOption {
+        default = null;
+        example = "/dev/zvol/tarta-zoot/swap-writeback";
+        type = lib.types.nullOr lib.types.path;
+        description = lib.mdDoc ''
+          Write incompressible pages to this device,
+          as there's no gain from keeping them in RAM.
         '';
       };
     };
 
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = cfg.writebackDevice == null || cfg.swapDevices <= 1;
+        message = "A single writeback device cannot be shared among multiple zram devices";
+      }
+    ];
 
-    inherit warnings;
 
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isModule "ZRAM")
@@ -127,76 +114,27 @@ in
     # once in stage 2 boot, and again when the zram-reloader service starts.
     # boot.kernelModules = [ "zram" ];
 
-    boot.extraModprobeConfig = ''
-      options zram num_devices=${toString cfg.numDevices}
-    '';
-
-    services.udev.extraRules = ''
-      KERNEL=="zram[0-9]*", ENV{SYSTEMD_WANTS}="zram-init-%k.service", TAG+="systemd"
-    '';
-
-    systemd.services =
-      let
-        createZramInitService = dev:
-          nameValuePair "zram-init-${dev}" {
-            description = "Init swap on zram-based device ${dev}";
-            after = [ "dev-${dev}.device" "zram-reloader.service" ];
-            requires = [ "dev-${dev}.device" "zram-reloader.service" ];
-            before = [ "dev-${dev}.swap" ];
-            requiredBy = [ "dev-${dev}.swap" ];
-            unitConfig.DefaultDependencies = false; # needed to prevent a cycle
-            serviceConfig = {
-              Type = "oneshot";
-              RemainAfterExit = true;
-              ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
-            };
-            script = ''
-              set -euo pipefail
-
-              # Calculate memory to use for zram
-              mem=$(${pkgs.gawk}/bin/awk '/MemTotal: / {
-                  value=int($2*${toString cfg.memoryPercent}/100.0/${toString devicesCount}*1024);
-                    ${lib.optionalString (cfg.memoryMax != null) ''
-                      memory_max=int(${toString cfg.memoryMax}/${toString devicesCount});
-                      if (value > memory_max) { value = memory_max }
-                    ''}
-                  print value
-              }' /proc/meminfo)
-
-              ${pkgs.util-linux}/sbin/zramctl --size $mem --algorithm ${cfg.algorithm} /dev/${dev}
-              ${pkgs.util-linux}/sbin/mkswap /dev/${dev}
-            '';
-            restartIfChanged = false;
-          };
-      in listToAttrs ((map createZramInitService devices) ++ [(nameValuePair "zram-reloader"
-        {
-          description = "Reload zram kernel module when number of devices changes";
-          wants = [ "systemd-udevd.service" ];
-          after = [ "systemd-udevd.service" ];
-          unitConfig.DefaultDependencies = false; # needed to prevent a cycle
-          serviceConfig = {
-            Type = "oneshot";
-            RemainAfterExit = true;
-            ExecStartPre = "${modprobe} -r zram";
-            ExecStart = "${modprobe} zram";
-            ExecStop = "${modprobe} -r zram";
-          };
-          restartTriggers = [
-            cfg.numDevices
-            cfg.algorithm
-            cfg.memoryPercent
-          ];
-          restartIfChanged = true;
-        })]);
-
-    swapDevices =
-      let
-        useZramSwap = dev:
-          {
-            device = "/dev/${dev}";
-            priority = cfg.priority;
-          };
-      in map useZramSwap devices;
+    systemd.packages = [ pkgs.zram-generator ];
+    systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap
+
+    environment.etc."systemd/zram-generator.conf".source =
+      (pkgs.formats.ini { }).generate "zram-generator.conf" (lib.listToAttrs
+        (builtins.map
+          (dev: {
+            name = dev;
+            value =
+              let
+                size = "${toString cfg.memoryPercent} / 100 * ram";
+              in
+              {
+                zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
+                compression-algorithm = cfg.algorithm;
+                swap-priority = cfg.priority;
+              } // lib.optionalAttrs (cfg.writebackDevice != null) {
+                writeback-device = cfg.writebackDevice;
+              };
+          })
+          devices));
 
   };