1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.zramSwap; 8 9 devices = map (nr: "zram${toString nr}") (range 0 (cfg.numDevices - 1)); 10 11 modprobe = "${config.system.sbin.modprobe}/sbin/modprobe"; 12 13in 14 15{ 16 17 ###### interface 18 19 options = { 20 21 zramSwap = { 22 23 enable = mkOption { 24 default = false; 25 type = types.bool; 26 description = '' 27 Enable in-memory compressed swap space provided by the zram kernel 28 module. It is recommended to enable only for kernel 3.14 or higher. 29 ''; 30 }; 31 32 numDevices = mkOption { 33 default = 4; 34 type = types.int; 35 description = '' 36 Number of zram swap devices to create. It should be equal to the 37 number of CPU cores your system has. 38 ''; 39 }; 40 41 memoryPercent = mkOption { 42 default = 50; 43 type = types.int; 44 description = '' 45 Maximum amount of memory that can be used by the zram swap devices 46 (as a percentage of your total memory). Defaults to 1/2 of your total 47 RAM. 48 ''; 49 }; 50 51 priority = mkOption { 52 default = 5; 53 type = types.int; 54 description = '' 55 Priority of the zram swap devices. It should be a number higher than 56 the priority of your disk-based swap devices (so that the system will 57 fill the zram swap devices before falling back to disk swap). 58 ''; 59 }; 60 61 }; 62 63 }; 64 65 config = mkIf cfg.enable { 66 67 system.requiredKernelConfig = with config.lib.kernelConfig; [ 68 (isModule "ZRAM") 69 ]; 70 71 # Disabling this for the moment, as it would create and mkswap devices twice, 72 # once in stage 2 boot, and again when the zram-reloader service starts. 73 # boot.kernelModules = [ "zram" ]; 74 75 boot.extraModprobeConfig = '' 76 options zram num_devices=${toString cfg.numDevices} 77 ''; 78 79 services.udev.extraRules = '' 80 KERNEL=="zram[0-9]*", ENV{SYSTEMD_WANTS}="zram-init-%k.service", TAG+="systemd" 81 ''; 82 83 systemd.services = 84 let 85 createZramInitService = dev: 86 nameValuePair "zram-init-${dev}" { 87 description = "Init swap on zram-based device ${dev}"; 88 bindsTo = [ "dev-${dev}.swap" ]; 89 after = [ "dev-${dev}.device" "zram-reloader.service" ]; 90 requires = [ "dev-${dev}.device" "zram-reloader.service" ]; 91 before = [ "dev-${dev}.swap" ]; 92 requiredBy = [ "dev-${dev}.swap" ]; 93 serviceConfig = { 94 Type = "oneshot"; 95 RemainAfterExit = true; 96 ExecStop = "${pkgs.stdenv.shell} -c 'echo 1 > /sys/class/block/${dev}/reset'"; 97 }; 98 script = '' 99 set -u 100 set -o pipefail 101 102 # Calculate memory to use for zram 103 totalmem=$(${pkgs.gnugrep}/bin/grep 'MemTotal: ' /proc/meminfo | ${pkgs.gawk}/bin/awk '{print $2}') 104 mem=$(((totalmem * ${toString cfg.memoryPercent} / 100 / ${toString cfg.numDevices}) * 1024)) 105 106 echo $mem > /sys/class/block/${dev}/disksize 107 ${pkgs.utillinux}/sbin/mkswap /dev/${dev} 108 ''; 109 restartIfChanged = false; 110 }; 111 in listToAttrs ((map createZramInitService devices) ++ [(nameValuePair "zram-reloader" 112 { 113 description = "Reload zram kernel module when number of devices changes"; 114 serviceConfig = { 115 Type = "oneshot"; 116 RemainAfterExit = true; 117 ExecStartPre = "${modprobe} -r zram"; 118 ExecStart = "${modprobe} zram"; 119 ExecStop = "${modprobe} -r zram"; 120 }; 121 restartTriggers = [ cfg.numDevices ]; 122 restartIfChanged = true; 123 })]); 124 125 swapDevices = 126 let 127 useZramSwap = dev: 128 { 129 device = "/dev/${dev}"; 130 priority = cfg.priority; 131 }; 132 in map useZramSwap devices; 133 134 }; 135 136}