Skip to content

Commit

Permalink
nixos/pid-fan-controller: init
Browse files Browse the repository at this point in the history
  • Loading branch information
zimward committed Sep 16, 2024
1 parent b1e78a9 commit 4137528
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@
./services/hardware/nvidia-optimus.nix
./services/hardware/openrgb.nix
./services/hardware/pcscd.nix
./services/hardware/pid-fan-controller.nix
./services/hardware/pommed.nix
./services/hardware/power-profiles-daemon.nix
./services/hardware/rasdaemon.nix
Expand Down
182 changes: 182 additions & 0 deletions nixos/modules/services/hardware/pid-fan-controller.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.services.pid-fan-controller;
heatSource = {
options = {
name = lib.mkOption {
type = lib.types.uniq lib.types.nonEmptyStr;
description = "name of heat source";
};
wildcard_path = lib.mkOption {
type = lib.types.nonEmptyStr;
description = ''wildcard path of heat source hwmon "temp_input", can contain wildcards'';
};
PID_params = {
set_point = lib.mkOption {
type = lib.types.ints.unsigned;
description = "set point of controller in °C";
};
P = lib.mkOption {
description = "K_p of PID controller";
type = lib.types.float;
};
I = lib.mkOption {
description = "K_i of PID controller";
type = lib.types.float;
};
D = lib.mkOption {
description = "K_d of PID controller";
type = lib.types.float;
};
};
};
};

fan = {
options = {
wildcard_path = lib.mkOption {
type = lib.types.str;
description = ''
wildcard path of the hwmon "pwm" file.
If the fans are not to be found in /sys/class/hwmon/hwmon* the corresponding
kernel module (like "nct6775") needs to be added to `boot.kernelModules`.
See: [hwmon](https://www.kernel.org/doc/html/latest/hwmon/index.html)
'';
};
min_pwm = lib.mkOption {
default = 0;
type = lib.types.ints.between 0 255;
description = "minimum PWM fan speed";
};
max_pwm = lib.mkOption {
default = 255;
type = lib.types.ints.between 0 255;
description = "maximum PWM fan speed";
};
cutoff = lib.mkOption {
default = false;
type = lib.types.bool;
description = "whether to stop fan when `min_pwm` is reached";
};
heat_pressure_srcs = lib.mkOption {
type = lib.types.nonEmptyListOf lib.types.str;
description = "heat pressure sources which are affected by the fan";
};
};
};
in
{
options.services.pid-fan-controller = {
enable = lib.mkEnableOption "Enable PID fan controller";
package = lib.mkPackageOption pkgs "pid-fan-controller" { };
settings = {
interval = lib.mkOption {
default = 500;
type = lib.types.int;
description = "Interval between controller cycles.";
};
heat_srcs = lib.mkOption {
type = lib.types.listOf (lib.types.submodule heatSource);
description = "list of heat sources";
example = ''
[
{
name = "cpu";
wildcard_path = "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon*/temp1_input";
PID_params = {
set_point = 60;
P = -5.0e-3;
I = -2.0e-3;
D = -6.0e-3;
};
}
{
name = "gpu";
wildcard_path = "/sys/class/drm/card*/device/hwmon/hwmon*/temp2_input";
PID_params = {
set_point = 65;
P = -5.0e-3;
I = -2.0e-3;
D = -6.0e-3;
};
}
];
'';
};
fans = lib.mkOption {
type = lib.types.listOf (lib.types.submodule fan);
description = "list of fans";
example = ''
[
{
wildcard_path = "/sys/devices/platform/nct6775.2592/hwmon/hwmon*/pwm1";
min_pwm = 60;
max_pwm = 255;
heat_pressure_srcs = [
"cpu"
"gpu"
];
}
{
wildcard_path = "/sys/devices/platform/nct6775.2592/hwmon/hwmon*/pwm4";
min_pwm = 60;
max_pwm = 200;
cutoff = true;
heat_pressure_srcs = [
"cpu"
];
}
];
'';
};
};
};
config = lib.mkIf cfg.enable {
environment.etc."pid-fan-settings.json".text = builtins.toJSON cfg.settings;

systemd.services.pid-fan-controller = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
ExecStart = [ "${cfg.package}/bin/pid-fan-controller" ];
ExecStopPost = [ "${cfg.package}/bin/pid-fan-controller disable" ];
Restart = "always";
#This service needs to run as root to write to /sys.
#therefore it should operate with the least amount of priviledges needed
ProtectHome = "yes";
#strict is not possible as it needs /sys
ProtectSystem = "full";
ProtectProc = "invisible";
PrivateNetwork = "yes";
NoNewPrivileges = "yes";
MemoryDenyWriteExecute = "yes";
RestrictNamespaces = "~user pid net uts mnt";
ProtectKernelModules = "yes";
RestrictRealtime = "yes";
SystemCallFilter = "@system-service";
CapabilityBoundingSet = "~CAP_KILL CAP_WAKE_ALARM CAP_IPC_LOC CAP_BPF CAP_LINUX_IMMUTABLE CAP_BLOCK_SUSPEND CAP_MKNOD";
};
# restart unit if config changed
restartTriggers = [ config.environment.etc."pid-fan-settings.json".text ];
};
#sleep hook to restart the service as it breaks sometimes otherwise
systemd.services.pid-fan-controller-sleep = {
before = [ "sleep.target" ];
wantedBy = [ "sleep.target" ];
unitConfig = {
StopWhenUnneeded = "yes";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = [ "${pkgs.systemd}/bin/systemctl stop pid-fan-controller.service" ];
ExecStop = [ "${pkgs.systemd}/bin/systemctl restart pid-fan-controller.service" ];
};
};
};
}

0 comments on commit 4137528

Please sign in to comment.