nixos/zram: use zram-generator
This commit is contained in:
parent
73b9fb8afa
commit
3d26221082
|
@ -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}
|
||||
'';
|
||||
systemd.packages = [ pkgs.zram-generator ];
|
||||
systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap
|
||||
|
||||
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 =
|
||||
environment.etc."systemd/zram-generator.conf".source =
|
||||
(pkgs.formats.ini { }).generate "zram-generator.conf" (lib.listToAttrs
|
||||
(builtins.map
|
||||
(dev: {
|
||||
name = dev;
|
||||
value =
|
||||
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"
|
||||
size = "${toString cfg.memoryPercent} / 100 * ram";
|
||||
in
|
||||
{
|
||||
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";
|
||||
zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
|
||||
compression-algorithm = cfg.algorithm;
|
||||
swap-priority = cfg.priority;
|
||||
};
|
||||
restartTriggers = [
|
||||
cfg.numDevices
|
||||
cfg.algorithm
|
||||
cfg.memoryPercent
|
||||
];
|
||||
restartIfChanged = true;
|
||||
})]);
|
||||
|
||||
swapDevices =
|
||||
let
|
||||
useZramSwap = dev:
|
||||
{
|
||||
device = "/dev/${dev}";
|
||||
priority = cfg.priority;
|
||||
};
|
||||
in map useZramSwap devices;
|
||||
})
|
||||
devices));
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
'';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue