Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/swapspace: init module #348588

Merged
merged 2 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@

- [tiny-dfr](https://github.com/WhatAmISupposedToPutHere/tiny-dfr), a dynamic function row daemon for the Touch Bar found on some Apple laptops. Available as [hardware.apple.touchBar.enable](options.html#opt-hardware.apple.touchBar.enable).

- [Swapspace](https://github.com/Tookmund/Swapspace), a dynamic swap space manager, turns your unused free space into swap automatically. Available as [services.swapspace](#opt-services.swapspace.enable).

## Backward Incompatibilities {#sec-release-24.11-incompatibilities}

- The `sound` options have been removed or renamed, as they had a lot of unintended side effects. See [below](#sec-release-24.11-migration-sound) for details.
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,7 @@
./services/system/nscd.nix
./services/system/saslauthd.nix
./services/system/self-deploy.nix
./services/system/swapspace.nix
./services/system/systembus-notify.nix
./services/system/systemd-lock-handler.nix
./services/system/uptimed.nix
Expand Down
120 changes: 120 additions & 0 deletions nixos/modules/services/system/swapspace.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{
config,
lib,
pkgs,
phanirithvij marked this conversation as resolved.
Show resolved Hide resolved
utils,
...
}:
let
cfg = config.services.swapspace;
inherit (lib)
types
mkOption
mkPackageOption
mkEnableOption
;
configFile = pkgs.writeText "swapspace.conf" (lib.generators.toKeyValue { } cfg.settings);
in
{
options.services.swapspace = {
enable = mkEnableOption "Swapspace, a dynamic swap space manager";
package = mkPackageOption pkgs "swapspace" { };
extraArgs = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"-P"
"-v"
];
description = "Any extra arguments to pass to swapspace";
};
settings = mkOption {
type = types.submodule {
options = {
swappath = mkOption {
type = types.str;
default = "/var/lib/swapspace";
description = "Location where swapspace may create and delete swapfiles";
};
lower_freelimit = mkOption {
type = types.ints.between 0 99;
default = 20;
description = "Lower free-space threshold: if the percentage of free space drops below this number, additional swapspace is allocated";
};
upper_freelimit = mkOption {
type = types.ints.between 0 100;
default = 60;
description = "Upper free-space threshold: if the percentage of free space exceeds this number, swapspace will attempt to free up swapspace";
};
freetarget = mkOption {
type = types.ints.between 2 99;
default = 30;
description = ''
Percentage of free space swapspace should aim for when adding swapspace.
This should fall somewhere between lower_freelimit and upper_freelimit.
'';
};
min_swapsize = mkOption {
type = types.str;
default = "4m";
description = "Smallest allowed size for individual swapfiles";
};
max_swapsize = mkOption {
type = types.str;
default = "2t";
description = "Greatest allowed size for individual swapfiles";
};
cooldown = mkOption {
type = types.ints.unsigned;
default = 600;
description = ''
Duration (roughly in seconds) of the moratorium on swap allocation that is instated if disk space runs out, or the cooldown time after a new swapfile is successfully allocated before swapspace will consider deallocating swap space again.
The default cooldown period is about 10 minutes.
'';
phanirithvij marked this conversation as resolved.
Show resolved Hide resolved
};
buffer_elasticity = mkOption {
type = types.ints.between 0 100;
default = 30;
description = ''Percentage of buffer space considered to be "free"'';
};
cache_elasticity = mkOption {
type = types.ints.between 0 100;
default = 80;
description = ''Percentage of cache space considered to be "free"'';
};
};
};
default = { };
description = ''
Config file for swapspace.
See the options here: <https://github.com/Tookmund/Swapspace/blob/master/swapspace.conf>
'';
};
};

config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.packages = [ cfg.package ];
systemd.services.swapspace = {
wantedBy = [ "multi-user.target" ];
phanirithvij marked this conversation as resolved.
Show resolved Hide resolved
serviceConfig = {
ExecStart = [
""
"${lib.getExe cfg.package} -c ${configFile} ${utils.escapeSystemdExecArgs cfg.extraArgs}"
];
};
};
systemd.tmpfiles.settings.swapspace = {
${cfg.settings.swappath}.d = {
mode = "0700";
};
};
};

meta = {
maintainers = with lib.maintainers; [
Luflosi
phanirithvij
];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,7 @@ in {
swap-file-btrfs = handleTest ./swap-file-btrfs.nix {};
swap-partition = handleTest ./swap-partition.nix {};
swap-random-encryption = handleTest ./swap-random-encryption.nix {};
swapspace = handleTestOn ["aarch64-linux" "x86_64-linux"] ./swapspace.nix {};
sway = handleTest ./sway.nix {};
swayfx = handleTest ./swayfx.nix {};
switchTest = handleTest ./switch-test.nix { ng = false; };
Expand Down
69 changes: 69 additions & 0 deletions nixos/tests/swapspace.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import ./make-test-python.nix (
{ pkgs, lib, ... }:

{
name = "swapspace";

meta = with pkgs.lib.maintainers; {
maintainers = [
Luflosi
phanirithvij
];
};

nodes.machine = {
virtualisation.memorySize = 512;

services.swapspace = {
enable = true;
extraArgs = [ "-v" ];
settings = {
# test outside /var/lib/swapspace
swappath = "/swamp";
cooldown = 1;
};
};

swapDevices = lib.mkOverride 0 [
{
size = 127;
device = "/root/swapfile";
}
];
boot.kernel.sysctl."vm.swappiness" = 60;
};

testScript = ''
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("swapspace.service")
machine.wait_for_unit("root-swapfile.swap")

swamp = False
with subtest("swapspace works"):
machine.execute("mkdir /root/memfs")
machine.execute("mount -o size=2G -t tmpfs none /root/memfs")
i = 0
while i < 14:
print(machine.succeed("free -h"))
out = machine.succeed("sh -c 'swapon --show --noheadings --raw --bytes | grep /root/swapfile'")
row = out.split(' ')
# leave 1MB free to not get killed by oom
freebytes=int(row[2]) - int(row[3]) - 1*1024*1024
machine.succeed(f"dd if=/dev/random of=/root/memfs/{i} bs={freebytes} count=1")
machine.sleep(1)
out = machine.succeed("swapon --show")
print(out)
swamp = "/swamp" in out
if not swamp:
i += 1
else:
print("*"*10, "SWAPED", "*"*10)
machine.succeed("rm -f /root/memfs/*")
break

print(machine.succeed("swapspace -e -s /swamp"))
assert "/swamp" not in machine.execute("swapon --show")
assert swamp
'';
}
)
15 changes: 14 additions & 1 deletion pkgs/tools/admin/swapspace/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
{ lib, stdenv, fetchFromGitHub, autoreconfHook, installShellFiles, util-linux, binlore, swapspace }:
{
lib,
stdenv,
fetchFromGitHub,
autoreconfHook,
installShellFiles,
util-linux,
binlore,
swapspace,
nixosTests,
}:

stdenv.mkDerivation rec {
pname = "swapspace";
Expand Down Expand Up @@ -40,6 +50,9 @@ stdenv.mkDerivation rec {
passthru.binlore.out = binlore.synthesize swapspace ''
execer cannot bin/swapspace
'';
passthru.tests = {
inherit (nixosTests) swapspace;
};

meta = with lib; {
description = "Dynamic swap manager for Linux";
Expand Down