Merge pull request #214103 from NickCao/zram

nixos/zram: use zram-generator

authored by

Florian Klink and committed by
GitHub
5aa52365 c326033c

+63 -131
+8
nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
··· 841 841 </listitem> 842 842 <listitem> 843 843 <para> 844 + The <literal>zramSwap</literal> is now implemented with 845 + <literal>zram-generator</literal>, and the option 846 + <literal>zramSwap.numDevices</literal> for using ZRAM devices 847 + as general purpose ephemeral block devices has been removed. 848 + </para> 849 + </listitem> 850 + <listitem> 851 + <para> 844 852 The <literal>unifi-poller</literal> package and corresponding 845 853 NixOS module have been renamed to <literal>unpoller</literal> 846 854 to match upstream.
+2
nixos/doc/manual/release-notes/rl-2305.section.md
··· 209 209 210 210 - The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)). 211 211 212 + - 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. 213 + 212 214 - The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream. 213 215 214 216 - 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.
+38 -122
nixos/modules/config/zram.nix
··· 1 1 { config, lib, pkgs, ... }: 2 2 3 - with lib; 4 - 5 3 let 6 4 7 5 cfg = config.zramSwap; 8 - 9 - # don't set swapDevices as mkDefault, so we can detect user had read our warning 10 - # (see below) and made an action (or not) 11 - devicesCount = if cfg.swapDevices != null then cfg.swapDevices else cfg.numDevices; 12 - 13 - devices = map (nr: "zram${toString nr}") (range 0 (devicesCount - 1)); 14 - 15 - modprobe = "${pkgs.kmod}/bin/modprobe"; 16 - 17 - warnings = 18 - assert cfg.swapDevices != null -> cfg.numDevices >= cfg.swapDevices; 19 - flatten [ 20 - (optional (cfg.numDevices > 1 && cfg.swapDevices == null) '' 21 - Using several small zram devices as swap is no better than using one large. 22 - Set either zramSwap.numDevices = 1 or explicitly set zramSwap.swapDevices. 23 - 24 - Previously multiple zram devices were used to enable multithreaded 25 - compression. Linux supports multithreaded compression for 1 device 26 - since 3.15. See https://lkml.org/lkml/2014/2/28/404 for details. 27 - '') 28 - ]; 6 + devices = map (nr: "zram${toString nr}") (lib.range 0 (cfg.swapDevices - 1)); 29 7 30 8 in 31 9 32 10 { 33 11 12 + imports = [ 13 + (lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported") 14 + ]; 15 + 34 16 ###### interface 35 17 36 18 options = { 37 19 38 20 zramSwap = { 39 21 40 - enable = mkOption { 22 + enable = lib.mkOption { 41 23 default = false; 42 - type = types.bool; 24 + type = lib.types.bool; 43 25 description = lib.mdDoc '' 44 26 Enable in-memory compressed devices and swap space provided by the zram 45 27 kernel module. ··· 49 31 ''; 50 32 }; 51 33 52 - numDevices = mkOption { 53 - default = 1; 54 - type = types.int; 55 - description = lib.mdDoc '' 56 - Number of zram devices to create. See also 57 - `zramSwap.swapDevices` 58 - ''; 59 - }; 60 - 61 - swapDevices = mkOption { 62 - default = null; 34 + swapDevices = lib.mkOption { 35 + default = 0; 63 36 example = 1; 64 - type = with types; nullOr int; 37 + type = lib.types.int; 65 38 description = lib.mdDoc '' 66 - Number of zram devices to be used as swap. Must be 67 - `<= zramSwap.numDevices`. 68 - Default is same as `zramSwap.numDevices`, recommended is 1. 39 + Number of zram devices to be used as swap, recommended is 1. 69 40 ''; 70 41 }; 71 42 72 - memoryPercent = mkOption { 43 + memoryPercent = lib.mkOption { 73 44 default = 50; 74 - type = types.int; 45 + type = lib.types.int; 75 46 description = lib.mdDoc '' 76 47 Maximum total amount of memory that can be stored in the zram swap devices 77 48 (as a percentage of your total memory). Defaults to 1/2 of your total ··· 80 51 ''; 81 52 }; 82 53 83 - memoryMax = mkOption { 54 + memoryMax = lib.mkOption { 84 55 default = null; 85 - type = with types; nullOr int; 56 + type = with lib.types; nullOr int; 86 57 description = lib.mdDoc '' 87 58 Maximum total amount of memory (in bytes) that can be stored in the zram 88 59 swap devices. ··· 90 61 ''; 91 62 }; 92 63 93 - priority = mkOption { 64 + priority = lib.mkOption { 94 65 default = 5; 95 - type = types.int; 66 + type = lib.types.int; 96 67 description = lib.mdDoc '' 97 68 Priority of the zram swap devices. It should be a number higher than 98 69 the priority of your disk-based swap devices (so that the system will ··· 100 71 ''; 101 72 }; 102 73 103 - algorithm = mkOption { 74 + algorithm = lib.mkOption { 104 75 default = "zstd"; 105 76 example = "lz4"; 106 - type = with types; either (enum [ "lzo" "lz4" "zstd" ]) str; 77 + type = with lib.types; either (enum [ "lzo" "lz4" "zstd" ]) str; 107 78 description = lib.mdDoc '' 108 79 Compression algorithm. `lzo` has good compression, 109 80 but is slow. `lz4` has bad compression, but is fast. ··· 116 87 117 88 }; 118 89 119 - config = mkIf cfg.enable { 120 - 121 - inherit warnings; 90 + config = lib.mkIf cfg.enable { 122 91 123 92 system.requiredKernelConfig = with config.lib.kernelConfig; [ 124 93 (isModule "ZRAM") ··· 128 97 # once in stage 2 boot, and again when the zram-reloader service starts. 129 98 # boot.kernelModules = [ "zram" ]; 130 99 131 - boot.extraModprobeConfig = '' 132 - options zram num_devices=${toString cfg.numDevices} 133 - ''; 134 - 135 - boot.kernelParams = ["zram.num_devices=${toString cfg.numDevices}"]; 136 - 137 - services.udev.extraRules = '' 138 - KERNEL=="zram[0-9]*", ENV{SYSTEMD_WANTS}="zram-init-%k.service", TAG+="systemd" 139 - ''; 140 - 141 - systemd.services = 142 - let 143 - createZramInitService = dev: 144 - nameValuePair "zram-init-${dev}" { 145 - description = "Init swap on zram-based device ${dev}"; 146 - after = [ "dev-${dev}.device" "zram-reloader.service" ]; 147 - requires = [ "dev-${dev}.device" "zram-reloader.service" ]; 148 - before = [ "dev-${dev}.swap" ]; 149 - requiredBy = [ "dev-${dev}.swap" ]; 150 - unitConfig.DefaultDependencies = false; # needed to prevent a cycle 151 - serviceConfig = { 152 - Type = "oneshot"; 153 - RemainAfterExit = true; 154 - ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'"; 155 - }; 156 - script = '' 157 - set -euo pipefail 158 - 159 - # Calculate memory to use for zram 160 - mem=$(${pkgs.gawk}/bin/awk '/MemTotal: / { 161 - value=int($2*${toString cfg.memoryPercent}/100.0/${toString devicesCount}*1024); 162 - ${lib.optionalString (cfg.memoryMax != null) '' 163 - memory_max=int(${toString cfg.memoryMax}/${toString devicesCount}); 164 - if (value > memory_max) { value = memory_max } 165 - ''} 166 - print value 167 - }' /proc/meminfo) 168 - 169 - ${pkgs.util-linux}/sbin/zramctl --size $mem --algorithm ${cfg.algorithm} /dev/${dev} 170 - ${pkgs.util-linux}/sbin/mkswap /dev/${dev} 171 - ''; 172 - restartIfChanged = false; 173 - }; 174 - in listToAttrs ((map createZramInitService devices) ++ [(nameValuePair "zram-reloader" 175 - { 176 - description = "Reload zram kernel module when number of devices changes"; 177 - wants = [ "systemd-udevd.service" ]; 178 - after = [ "systemd-udevd.service" ]; 179 - unitConfig.DefaultDependencies = false; # needed to prevent a cycle 180 - serviceConfig = { 181 - Type = "oneshot"; 182 - RemainAfterExit = true; 183 - ExecStartPre = "-${modprobe} -r zram"; 184 - ExecStart = "-${modprobe} zram"; 185 - ExecStop = "-${modprobe} -r zram"; 186 - }; 187 - restartTriggers = [ 188 - cfg.numDevices 189 - cfg.algorithm 190 - cfg.memoryPercent 191 - ]; 192 - restartIfChanged = true; 193 - })]); 100 + systemd.packages = [ pkgs.zram-generator ]; 101 + systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap 194 102 195 - swapDevices = 196 - let 197 - useZramSwap = dev: 198 - { 199 - device = "/dev/${dev}"; 200 - priority = cfg.priority; 201 - }; 202 - in map useZramSwap devices; 103 + environment.etc."systemd/zram-generator.conf".source = 104 + (pkgs.formats.ini { }).generate "zram-generator.conf" (lib.listToAttrs 105 + (builtins.map 106 + (dev: { 107 + name = dev; 108 + value = 109 + let 110 + size = "${toString cfg.memoryPercent} / 100 * ram"; 111 + in 112 + { 113 + zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size; 114 + compression-algorithm = cfg.algorithm; 115 + swap-priority = cfg.priority; 116 + }; 117 + }) 118 + devices)); 203 119 204 120 }; 205 121
+15 -9
nixos/tests/zram-generator.nix
··· 1 1 import ./make-test-python.nix { 2 2 name = "zram-generator"; 3 3 4 - nodes.machine = { pkgs, ... }: { 5 - environment.etc."systemd/zram-generator.conf".text = '' 6 - [zram0] 7 - zram-size = ram / 2 8 - ''; 9 - systemd.packages = [ pkgs.zram-generator ]; 10 - systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap 4 + nodes.machine = { ... }: { 5 + zramSwap = { 6 + enable = true; 7 + priority = 10; 8 + algorithm = "lz4"; 9 + swapDevices = 2; 10 + memoryPercent = 30; 11 + memoryMax = 10 * 1024 * 1024; 12 + }; 11 13 }; 12 14 13 15 testScript = '' 14 16 machine.wait_for_unit("systemd-zram-setup@zram0.service") 15 - assert "zram0" in machine.succeed("zramctl -n") 16 - assert "zram0" in machine.succeed("swapon --show --noheadings") 17 + machine.wait_for_unit("systemd-zram-setup@zram1.service") 18 + zram = machine.succeed("zramctl --noheadings --raw") 19 + swap = machine.succeed("swapon --show --noheadings") 20 + for i in range(2): 21 + assert f"/dev/zram{i} lz4 10M" in zram 22 + assert f"/dev/zram{i} partition 10M" in swap 17 23 ''; 18 24 }