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 </listitem> 842 <listitem> 843 <para> 844 The <literal>unifi-poller</literal> package and corresponding 845 NixOS module have been renamed to <literal>unpoller</literal> 846 to match upstream.
··· 841 </listitem> 842 <listitem> 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> 852 The <literal>unifi-poller</literal> package and corresponding 853 NixOS module have been renamed to <literal>unpoller</literal> 854 to match upstream.
+2
nixos/doc/manual/release-notes/rl-2305.section.md
··· 209 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 212 - The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream. 213 214 - 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.
··· 209 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 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 + 214 - The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream. 215 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 { config, lib, pkgs, ... }: 2 3 - with lib; 4 - 5 let 6 7 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 - ]; 29 30 in 31 32 { 33 34 ###### interface 35 36 options = { 37 38 zramSwap = { 39 40 - enable = mkOption { 41 default = false; 42 - type = types.bool; 43 description = lib.mdDoc '' 44 Enable in-memory compressed devices and swap space provided by the zram 45 kernel module. ··· 49 ''; 50 }; 51 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; 63 example = 1; 64 - type = with types; nullOr int; 65 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. 69 ''; 70 }; 71 72 - memoryPercent = mkOption { 73 default = 50; 74 - type = types.int; 75 description = lib.mdDoc '' 76 Maximum total amount of memory that can be stored in the zram swap devices 77 (as a percentage of your total memory). Defaults to 1/2 of your total ··· 80 ''; 81 }; 82 83 - memoryMax = mkOption { 84 default = null; 85 - type = with types; nullOr int; 86 description = lib.mdDoc '' 87 Maximum total amount of memory (in bytes) that can be stored in the zram 88 swap devices. ··· 90 ''; 91 }; 92 93 - priority = mkOption { 94 default = 5; 95 - type = types.int; 96 description = lib.mdDoc '' 97 Priority of the zram swap devices. It should be a number higher than 98 the priority of your disk-based swap devices (so that the system will ··· 100 ''; 101 }; 102 103 - algorithm = mkOption { 104 default = "zstd"; 105 example = "lz4"; 106 - type = with types; either (enum [ "lzo" "lz4" "zstd" ]) str; 107 description = lib.mdDoc '' 108 Compression algorithm. `lzo` has good compression, 109 but is slow. `lz4` has bad compression, but is fast. ··· 116 117 }; 118 119 - config = mkIf cfg.enable { 120 - 121 - inherit warnings; 122 123 system.requiredKernelConfig = with config.lib.kernelConfig; [ 124 (isModule "ZRAM") ··· 128 # once in stage 2 boot, and again when the zram-reloader service starts. 129 # boot.kernelModules = [ "zram" ]; 130 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 - })]); 194 195 - swapDevices = 196 - let 197 - useZramSwap = dev: 198 - { 199 - device = "/dev/${dev}"; 200 - priority = cfg.priority; 201 - }; 202 - in map useZramSwap devices; 203 204 }; 205
··· 1 { config, lib, pkgs, ... }: 2 3 let 4 5 cfg = config.zramSwap; 6 + devices = map (nr: "zram${toString nr}") (lib.range 0 (cfg.swapDevices - 1)); 7 8 in 9 10 { 11 12 + imports = [ 13 + (lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported") 14 + ]; 15 + 16 ###### interface 17 18 options = { 19 20 zramSwap = { 21 22 + enable = lib.mkOption { 23 default = false; 24 + type = lib.types.bool; 25 description = lib.mdDoc '' 26 Enable in-memory compressed devices and swap space provided by the zram 27 kernel module. ··· 31 ''; 32 }; 33 34 + swapDevices = lib.mkOption { 35 + default = 0; 36 example = 1; 37 + type = lib.types.int; 38 description = lib.mdDoc '' 39 + Number of zram devices to be used as swap, recommended is 1. 40 ''; 41 }; 42 43 + memoryPercent = lib.mkOption { 44 default = 50; 45 + type = lib.types.int; 46 description = lib.mdDoc '' 47 Maximum total amount of memory that can be stored in the zram swap devices 48 (as a percentage of your total memory). Defaults to 1/2 of your total ··· 51 ''; 52 }; 53 54 + memoryMax = lib.mkOption { 55 default = null; 56 + type = with lib.types; nullOr int; 57 description = lib.mdDoc '' 58 Maximum total amount of memory (in bytes) that can be stored in the zram 59 swap devices. ··· 61 ''; 62 }; 63 64 + priority = lib.mkOption { 65 default = 5; 66 + type = lib.types.int; 67 description = lib.mdDoc '' 68 Priority of the zram swap devices. It should be a number higher than 69 the priority of your disk-based swap devices (so that the system will ··· 71 ''; 72 }; 73 74 + algorithm = lib.mkOption { 75 default = "zstd"; 76 example = "lz4"; 77 + type = with lib.types; either (enum [ "lzo" "lz4" "zstd" ]) str; 78 description = lib.mdDoc '' 79 Compression algorithm. `lzo` has good compression, 80 but is slow. `lz4` has bad compression, but is fast. ··· 87 88 }; 89 90 + config = lib.mkIf cfg.enable { 91 92 system.requiredKernelConfig = with config.lib.kernelConfig; [ 93 (isModule "ZRAM") ··· 97 # once in stage 2 boot, and again when the zram-reloader service starts. 98 # boot.kernelModules = [ "zram" ]; 99 100 + systemd.packages = [ pkgs.zram-generator ]; 101 + systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap 102 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)); 119 120 }; 121
+15 -9
nixos/tests/zram-generator.nix
··· 1 import ./make-test-python.nix { 2 name = "zram-generator"; 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 11 }; 12 13 testScript = '' 14 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 ''; 18 }
··· 1 import ./make-test-python.nix { 2 name = "zram-generator"; 3 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 + }; 13 }; 14 15 testScript = '' 16 machine.wait_for_unit("systemd-zram-setup@zram0.service") 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 23 ''; 24 }