-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmodule.nix
More file actions
226 lines (198 loc) · 8.53 KB
/
Copy pathmodule.nix
File metadata and controls
226 lines (198 loc) · 8.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# NixOS module for CoreCycler — per-core CPU stability tester and
# PBO Curve Optimizer tuner for AMD Ryzen.
#
# Handles all kernel modules needed for monitoring and SMU access:
# Out-of-tree: ryzen_smu (AMD SMU), zenpower5 (AMD hwmon), it87 (ITE Super I/O)
# In-tree: msr, nct6775 (Nuvoton Super I/O), coretemp (Intel), cpuid
#
# Also handles device access (udev, systemd oneshot, group) and the corecycler package.
#
# Usage in a consumer flake:
# imports = [ inputs.linux-corecycler.nixosModules.default ];
# services.corecycler = {
# enable = true;
# deviceAccessUser = "myuser";
# };
{ self }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.corecycler;
inherit (pkgs.stdenv.hostPlatform) system;
package =
if cfg.unfreeBackends then self.packages.${system}.full else self.packages.${system}.default;
zenpowerPkg = pkgs.callPackage ./zenpower.nix {
inherit (config.boot.kernelPackages) kernel;
};
ryzenSmuPkg = pkgs.callPackage ./ryzen-smu.nix {
inherit (config.boot.kernelPackages) kernel;
};
it87Pkg = pkgs.callPackage ./it87.nix {
inherit (config.boot.kernelPackages) kernel;
};
in
{
_class = "nixos";
options.services.corecycler = {
enable = lib.mkEnableOption "CoreCycler per-core CPU stability tester and PBO Curve Optimizer tuner";
unfreeBackends = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to include unfree backends (mprime). When false, only FOSS backends (stress-ng) are bundled.";
};
# --- AMD SMU access ---
ryzenSmu = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to load the ryzen_smu kernel module (amkillam fork) for Curve Optimizer read/write via SMU. Supports Zen 1 through Zen 5.";
};
# --- CPU hwmon drivers ---
zenpower = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to use zenpower5 instead of k10temp for AMD CPU monitoring. Provides Tctl/Tdie/Tccd temps, SVI2 voltage/current (Zen 1-4), and RAPL power. Replaces k10temp (blacklisted). Zen 1 through Zen 5.";
};
coretemp = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load the in-tree coretemp module for Intel CPU temperature monitoring. Per-core and per-package DTS readings. Only needed on Intel systems.";
};
# --- Super I/O (motherboard voltage/fan/temp) ---
nct6775 = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load the in-tree nct6775 module for Nuvoton NCT6775–NCT6799 Super I/O chips. Provides motherboard Vcore, fan speeds, and temperatures. Common on ASUS, MSI, ASRock boards. Needed for Zen 5 Vcore fallback on Nuvoton boards.";
};
nct6683 = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load the in-tree nct6683 module for Nuvoton NCT6683/NCT6686/NCT6687 Super I/O chips. Common on modern MSI boards (B550, B650, X570, X670). Needed for Zen 5 Vcore fallback when nct6775 does not cover your chip.";
};
it87 = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load the out-of-tree it87 module (frankcrawford fork) for ITE Super I/O chips. Provides motherboard Vcore (in0), fan speeds, and temperatures. Common on Gigabyte boards. Supports 38+ chip models including IT8686E, IT8689E. Needed for Zen 5 Vcore fallback on ITE boards.";
};
# --- Utility modules ---
cpuid = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load the in-tree cpuid module. Exposes /dev/cpu/*/cpuid for CPUID leaf access. Useful for CPU topology and feature detection.";
};
spd5118 = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to load spd5118 and i2c_dev modules for DDR5 DIMM temperature monitoring via the SPD5118 hub chip.";
};
# --- Device access ---
deviceAccess = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to grant the deviceAccessUser access to MSR devices and SMU sysfs via a dedicated group and udev rules. No sudo required for monitoring and CO access.";
};
deviceAccessUser = lib.mkOption {
type = lib.types.str;
default = "";
description = "Username to grant device access to (added to the corecycler group). Required when deviceAccess is true.";
};
autoResume = {
enable = lib.mkEnableOption "resuming the active tuner session automatically after login (freeze-and-continue without clicks). Runs sudo-less via the corecycler device-access group.";
delaySeconds = lib.mkOption {
type = lib.types.ints.positive;
default = 120;
description = "Settle time after login before the session resumes.";
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.deviceAccess -> cfg.deviceAccessUser != "";
message = "services.corecycler.deviceAccessUser must be set when deviceAccess is enabled.";
}
];
environment.systemPackages = [ package ];
# Login autostart: the app itself enforces the guards (mid-run sessions
# only, single-instance lock, settle delay).
environment.etc."xdg/autostart/corecycler-autoresume.desktop" = lib.mkIf cfg.autoResume.enable {
text = ''
[Desktop Entry]
Type=Application
Name=CoreCycler auto-resume
Exec=${lib.getExe package} --auto-resume ${toString cfg.autoResume.delaySeconds}
X-GNOME-Autostart-enabled=true
'';
};
# --- Device access via dedicated group (no sudo) ---
users.groups.corecycler = lib.mkIf cfg.deviceAccess { };
users.users = lib.optionalAttrs (cfg.deviceAccess && cfg.deviceAccessUser != "") {
${cfg.deviceAccessUser}.extraGroups = [ "corecycler" ];
};
# MSR devices: grant group read access for APERF/MPERF (clock stretch)
# and RAPL energy counters (per-core + package power)
services.udev.extraRules = lib.mkIf cfg.deviceAccess ''
SUBSYSTEM=="msr", KERNEL=="msr[0-9]*", GROUP="corecycler", MODE="0640"
'';
# --- Kernel modules ---
# In-tree modules loaded via boot.kernelModules, out-of-tree via extraModulePackages
boot.kernelModules = [
"msr" # always needed for APERF/MPERF and RAPL MSR access
]
++ lib.optional cfg.ryzenSmu "ryzen_smu"
++ lib.optional cfg.zenpower "zenpower"
++ lib.optional cfg.coretemp "coretemp"
++ lib.optional cfg.nct6775 "nct6775"
++ lib.optional cfg.nct6683 "nct6683"
++ lib.optional cfg.it87 "it87"
++ lib.optional cfg.cpuid "cpuid"
++ lib.optionals cfg.spd5118 [
"i2c_dev"
"spd5118"
];
# Out-of-tree kernel modules — custom derivations that build with
# clang/LLVM when kernel makeFlags indicate LLVM build, gcc otherwise
boot.extraModulePackages =
lib.optional cfg.ryzenSmu ryzenSmuPkg
++ lib.optional cfg.zenpower zenpowerPkg
++ lib.optional cfg.it87 it87Pkg;
# Blacklist k10temp when zenpower is used (they conflict — same PCI device)
boot.blacklistedKernelModules = lib.mkIf cfg.zenpower [ "k10temp" ];
# SMU sysfs: grant group read/write for Curve Optimizer access.
# A systemd oneshot is used because tmpfiles z-rules and udev module events
# both race with sysfs creation in module_init(). ConditionPathExists +
# After=systemd-modules-load.service guarantees paths exist.
systemd.services.corecycler-smu-permissions = lib.mkIf (cfg.deviceAccess && cfg.ryzenSmu) {
description = "Set ryzen_smu sysfs permissions for corecycler group";
after = [ "systemd-modules-load.service" ];
wantedBy = [ "multi-user.target" ];
unitConfig.ConditionPathExists = "/sys/kernel/ryzen_smu_drv/smu_args";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart =
let
paths = [
"/sys/kernel/ryzen_smu_drv/smu_args"
"/sys/kernel/ryzen_smu_drv/mp1_smu_cmd"
"/sys/kernel/ryzen_smu_drv/rsmu_cmd"
];
in
pkgs.writeShellScript "corecycler-smu-perms" ''
set -euo pipefail
for f in ${lib.concatStringsSep " " paths}; do
chgrp corecycler "$f"
chmod 0660 "$f"
done
'';
};
};
# Allow unprivileged dmesg access for MCE error detection
boot.kernel.sysctl = lib.mkIf cfg.deviceAccess {
"kernel.dmesg_restrict" = lib.mkDefault 0;
};
};
}