about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-02-06 12:53:19 +0100
committerGitHub <noreply@github.com>2023-02-06 12:53:19 +0100
commit5aa52365e724f6dcccad9961f3da775947496b0e (patch)
treef44888d70d3c1f417e49de2d30c06f1f2446aa8b
parentc326033c381ae71ed5a81cc07c6619017d4bfff4 (diff)
parent989b9901dc7d1d72f1591d05c3cef074798eb896 (diff)
Merge pull request #214103 from NickCao/zram
nixos/zram: use zram-generator
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2305.section.xml8
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md2
-rw-r--r--nixos/modules/config/zram.nix162
-rw-r--r--nixos/tests/zram-generator.nix24
4 files changed, 64 insertions, 132 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
index 27b7feaef838a..d573acfdd3b7f 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
@@ -841,6 +841,14 @@
       </listitem>
       <listitem>
         <para>
+          The <literal>zramSwap</literal> is now implemented with
+          <literal>zram-generator</literal>, and the option
+          <literal>zramSwap.numDevices</literal> for using ZRAM devices
+          as general purpose ephemeral block devices has been removed.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>unifi-poller</literal> package and corresponding
           NixOS module have been renamed to <literal>unpoller</literal>
           to match upstream.
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index 0f827b4a6db4a..27bfdd5df66ff 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -209,6 +209,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)).
 
+- The `zramSwap` is now implemented with `zram-generator`, and the option `zramSwap.numDevices` for using ZRAM devices as general purpose ephemeral block devices has been removed.
+
 - The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream.
 
 - The new option `services.tailscale.useRoutingFeatures` controls various settings for using Tailscale features like exit nodes and subnet routers. If you wish to use your machine as an exit node, you can set this setting to `server`, otherwise if you wish to use an exit node you can set this setting to `client`. The strict RPF warning has been removed as the RPF will be loosened automatically based on the value of this setting.
diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix
index 87ac53a60b7ed..7b4639d8b1c8a 100644
--- a/nixos/modules/config/zram.nix
+++ b/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,29 +31,18 @@ in
         '';
       };
 
-      numDevices = mkOption {
-        default = 1;
-        type = types.int;
-        description = lib.mdDoc ''
-          Number of zram devices to create. See also
-          `zramSwap.swapDevices`
-        '';
-      };
-
-      swapDevices = mkOption {
-        default = null;
+      swapDevices = lib.mkOption {
+        default = 0;
         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 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
@@ -80,9 +51,9 @@ in
         '';
       };
 
-      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 stored in the zram
           swap devices.
@@ -90,9 +61,9 @@ in
         '';
       };
 
-      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
@@ -100,10 +71,10 @@ in
         '';
       };
 
-      algorithm = mkOption {
+      algorithm = lib.mkOption {
         default = "zstd";
         example = "lz4";
-        type = with types; either (enum [ "lzo" "lz4" "zstd" ]) str;
+        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.
@@ -116,9 +87,7 @@ in
 
   };
 
-  config = mkIf cfg.enable {
-
-    inherit warnings;
+  config = lib.mkIf cfg.enable {
 
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isModule "ZRAM")
@@ -128,78 +97,25 @@ 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}
-    '';
-
-    boot.kernelParams = ["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;
+              };
+          })
+          devices));
 
   };
 
diff --git a/nixos/tests/zram-generator.nix b/nixos/tests/zram-generator.nix
index affa081bcc353..3407361d2824f 100644
--- a/nixos/tests/zram-generator.nix
+++ b/nixos/tests/zram-generator.nix
@@ -1,18 +1,24 @@
 import ./make-test-python.nix {
   name = "zram-generator";
 
-  nodes.machine = { pkgs, ... }: {
-    environment.etc."systemd/zram-generator.conf".text = ''
-      [zram0]
-      zram-size = ram / 2
-    '';
-    systemd.packages = [ pkgs.zram-generator ];
-    systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap
+  nodes.machine = { ... }: {
+    zramSwap = {
+      enable = true;
+      priority = 10;
+      algorithm = "lz4";
+      swapDevices = 2;
+      memoryPercent = 30;
+      memoryMax = 10 * 1024 * 1024;
+    };
   };
 
   testScript = ''
     machine.wait_for_unit("systemd-zram-setup@zram0.service")
-    assert "zram0" in machine.succeed("zramctl -n")
-    assert "zram0" in machine.succeed("swapon --show --noheadings")
+    machine.wait_for_unit("systemd-zram-setup@zram1.service")
+    zram = machine.succeed("zramctl --noheadings --raw")
+    swap = machine.succeed("swapon --show --noheadings")
+    for i in range(2):
+        assert f"/dev/zram{i} lz4 10M" in zram
+        assert f"/dev/zram{i} partition  10M" in swap
   '';
 }