Unverified Commit 5aa52365 authored by Florian Klink's avatar Florian Klink Committed by GitHub
Browse files

Merge pull request #214103 from NickCao/zram

nixos/zram: use zram-generator
parents c326033c 989b9901
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -839,6 +839,14 @@
          (<link linkend="opt-services.fwupd.daemonSettings"><literal>services.fwupd.daemonSettings</literal></link>).
        </para>
      </listitem>
      <listitem>
        <para>
          The <literal>zramSwap</literal> is now implemented with
          <literal>zram-generator</literal>, and the option
          <literal>zramSwap.numDevices</literal> for using ZRAM devices
          as general purpose ephemeral block devices has been removed.
        </para>
      </listitem>
      <listitem>
        <para>
          The <literal>unifi-poller</literal> package and corresponding
+2 −0
Original line number Diff line number Diff line
@@ -209,6 +209,8 @@ In addition to numerous new and upgraded packages, this release has the followin

- The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)).

- 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.

- The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream.

- 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.
+39 −123
Original line number Diff line number Diff line
{ 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}
    '';

    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 =
      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"
        {
          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";
          };
          restartTriggers = [
            cfg.numDevices
            cfg.algorithm
            cfg.memoryPercent
          ];
          restartIfChanged = true;
        })]);
    systemd.packages = [ pkgs.zram-generator ];
    systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap

    swapDevices =
    environment.etc."systemd/zram-generator.conf".source =
      (pkgs.formats.ini { }).generate "zram-generator.conf" (lib.listToAttrs
        (builtins.map
          (dev: {
            name = dev;
            value =
              let
        useZramSwap = dev:
                size = "${toString cfg.memoryPercent} / 100 * ram";
              in
              {
            device = "/dev/${dev}";
            priority = cfg.priority;
                zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
                compression-algorithm = cfg.algorithm;
                swap-priority = cfg.priority;
              };
      in map useZramSwap devices;
          })
          devices));

  };

+15 −9
Original line number Diff line number Diff line
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
  '';
}