summary refs log tree commit diff
diff options
context:
space:
mode:
authordanbst <abcz2.uprola@gmail.com>2018-12-28 00:00:48 +0200
committerdanbst <abcz2.uprola@gmail.com>2019-01-17 15:58:53 +0200
commit8d8a7210e480ac122d55a7d36879b8f41ea6fc80 (patch)
treee703f5cf360f84cd6ad819c7ecd7237173c101fb
parent872502aa56bd4d574fcfe9cfef9066c9e8ee2894 (diff)
downloadnixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar.gz
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar.bz2
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar.lz
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar.xz
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.tar.zst
nixpkgs-8d8a7210e480ac122d55a7d36879b8f41ea6fc80.zip
zramSwap: allow configure compression algorithm + cleanups
- add `zramSwap.algorithm` option, which allows to change compressor
declaratively. zstd as default
- add `zramSwap.swapDevices` option, which allows to define how many zram
devices will be used as swap. Rest devices can be managed freely
- simpler floating calculations
- fix udev race condition
- some documentation changes
- replaced `/sys/block/zram*` handling with `zramctl`, because I had occasional
"Device is busy" error (looks like zram has to be configured in predefined order)
- added `memoryPercent` and `algorithm` as restart triggers. I think, it was
a bug that changing `memoryPercent` in configuration wasn't applied immediately.
- removed a bind to .swap device. While it looks natural (when swap device goes
off, so should zram device), it wasn't implemented properly. This caused problems
with swapon/swapoff:
```
$ cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/zram0                              partition       8166024 0       -2
/var/swapfile                           file            5119996 5120    1

$ sudo swapoff -a

$ sudo swapon -a
swapon: /dev/zram0: read swap header failed

$ cat /proc/swaps
Filename                                Type            Size    Used    Priority
/var/swapfile                           file            5119996 0       1
```
-rw-r--r--nixos/doc/manual/release-notes/rl-1903.xml15
-rw-r--r--nixos/modules/config/zram.nix79
2 files changed, 80 insertions, 14 deletions
diff --git a/nixos/doc/manual/release-notes/rl-1903.xml b/nixos/doc/manual/release-notes/rl-1903.xml
index 89d9f48aedd..da3b75cf614 100644
--- a/nixos/doc/manual/release-notes/rl-1903.xml
+++ b/nixos/doc/manual/release-notes/rl-1903.xml
@@ -408,6 +408,21 @@
        from nixpkgs due to the lack of maintainers.
      </para>
    </listitem>
+   <listitem>
+     <para>
+       It is possible now to uze ZRAM devices as general purpose ephemeral block devices,
+       not only as swap. Using more than 1 device as ZRAM swap is no longer recommended,
+       but is still possible by setting <literal>zramSwap.swapDevices</literal> explicitly.
+     </para>
+     <para>
+      Default algorithm for ZRAM swap was changed to <literal>zstd</literal>.
+     </para>
+     <para>
+      Changes to ZRAM algorithm are applied during <literal>nixos-rebuild switch</literal>,
+      so make sure you have enough swap space on disk to survive ZRAM device rebuild. Alternatively,
+      use <literal>nixos-rebuild boot; reboot</literal>.
+     </para>
+   </listitem>
   </itemizedlist>
  </section>
 </section>
diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix
index c1748812821..f8ff926cd56 100644
--- a/nixos/modules/config/zram.nix
+++ b/nixos/modules/config/zram.nix
@@ -6,10 +6,27 @@ let
 
   cfg = config.zramSwap;
 
-  devices = map (nr: "zram${toString nr}") (range 0 (cfg.numDevices - 1));
+  # 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.
+    '')
+  ];
+
 in
 
 {
@@ -24,9 +41,11 @@ in
         default = false;
         type = types.bool;
         description = ''
-          Enable in-memory compressed swap space provided by the zram kernel
-          module.
-          See https://www.kernel.org/doc/Documentation/blockdev/zram.txt
+          Enable in-memory compressed devices and swap space provided by the zram
+          kernel module.
+          See <link xlink:href="https://www.kernel.org/doc/Documentation/blockdev/zram.txt">
+            https://www.kernel.org/doc/Documentation/blockdev/zram.txt
+          </link>.
         '';
       };
 
@@ -34,7 +53,19 @@ in
         default = 1;
         type = types.int;
         description = ''
-          Number of zram swap devices to create.
+          Number of zram devices to create. See also
+          <literal>zramSwap.swapDevices</literal>
+        '';
+      };
+
+      swapDevices = mkOption {
+        default = null;
+        example = 1;
+        type = with types; nullOr int;
+        description = ''
+          Number of zram devices to be used as swap. Must be
+          <literal>&lt;= zramSwap.numDevices</literal>.
+          Default is same as <literal>zramSwap.numDevices</literal>, recommended is 1.
         '';
       };
 
@@ -44,7 +75,8 @@ in
         description = ''
           Maximum amount of memory that can be used by the zram swap devices
           (as a percentage of your total memory). Defaults to 1/2 of your total
-          RAM.
+          RAM. Run <literal>zramctl</literal> to check how good memory is
+          compressed.
         '';
       };
 
@@ -58,12 +90,26 @@ in
         '';
       };
 
+      algorithm = mkOption {
+        default = "zstd";
+        example = "lzo";
+        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.
+          You can check what other algorithms are supported by your zram device with
+          <programlisting>cat /sys/class/block/zram*/comp_algorithm</programlisting>
+        '';
+      };
     };
 
   };
 
   config = mkIf cfg.enable {
 
+    inherit warnings;
+
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isModule "ZRAM")
     ];
@@ -85,7 +131,6 @@ in
         createZramInitService = dev:
           nameValuePair "zram-init-${dev}" {
             description = "Init swap on zram-based device ${dev}";
-            bindsTo = [ "dev-${dev}.swap" ];
             after = [ "dev-${dev}.device" "zram-reloader.service" ];
             requires = [ "dev-${dev}.device" "zram-reloader.service" ];
             before = [ "dev-${dev}.swap" ];
@@ -96,14 +141,14 @@ in
               ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
             };
             script = ''
-              set -u
-              set -o pipefail
-              
+              set -euo pipefail
+
               # Calculate memory to use for zram
-              totalmem=$(${pkgs.gnugrep}/bin/grep 'MemTotal: ' /proc/meminfo | ${pkgs.gawk}/bin/awk '{print $2}')
-              mem=$(((totalmem * ${toString cfg.memoryPercent} / 100 / ${toString cfg.numDevices}) * 1024))
+              mem=$(${pkgs.gawk}/bin/awk '/MemTotal: / {
+                  print int($2*${toString cfg.memoryPercent}/100.0/${toString devicesCount}*1024)
+              }' /proc/meminfo)
 
-              echo $mem > /sys/class/block/${dev}/disksize
+              ${pkgs.utillinux}/sbin/zramctl --size $mem --algorithm ${cfg.algorithm} /dev/${dev}
               ${pkgs.utillinux}/sbin/mkswap /dev/${dev}
             '';
             restartIfChanged = false;
@@ -111,6 +156,8 @@ in
       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" ];
           serviceConfig = {
             Type = "oneshot";
             RemainAfterExit = true;
@@ -118,7 +165,11 @@ in
             ExecStart = "${modprobe} zram";
             ExecStop = "${modprobe} -r zram";
           };
-          restartTriggers = [ cfg.numDevices ];
+          restartTriggers = [
+            cfg.numDevices
+            cfg.algorithm
+            cfg.memoryPercent
+          ];
           restartIfChanged = true;
         })]);