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 ```
This commit is contained in:
parent
872502aa56
commit
8d8a7210e4
@ -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>
|
||||
|
@ -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><= 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
|
||||
|
||||
# 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))
|
||||
set -euo pipefail
|
||||
|
||||
echo $mem > /sys/class/block/${dev}/disksize
|
||||
# Calculate memory to use for zram
|
||||
mem=$(${pkgs.gawk}/bin/awk '/MemTotal: / {
|
||||
print int($2*${toString cfg.memoryPercent}/100.0/${toString devicesCount}*1024)
|
||||
}' /proc/meminfo)
|
||||
|
||||
${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;
|
||||
})]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user