dot
vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks
| 1 | # What CachyOS adds on top of plain Arch: its kernel and the cachyos-settings |
| 2 | # package (sysctl, udev rules, systemd defaults), ported file by file. The CachyOS |
| 3 | # file each part comes from is named next to it, so they can be compared when |
| 4 | # CachyOS changes something. |
| 5 | # |
| 6 | # Not ported, because it does nothing on this machine: the laptop audio power |
| 7 | # saving and touchpad rules, the old-Radeon (SI/CIK) amdgpu options, and |
| 8 | # debuginfod. Snapper root snapshots are replaced by NixOS generations: every |
| 9 | # rebuild is a boot menu entry you can go back to. |
| 10 | { |
| 11 | inputs, |
| 12 | lib, |
| 13 | pkgs, |
| 14 | ... |
| 15 | }: |
| 16 | let |
| 17 | # cachyos-settings: /usr/bin/game-performance. Runs a game with the |
| 18 | # "performance" power profile held, and the screensaver inhibited. Use it as |
| 19 | # a Steam launch option: game-performance %command% |
| 20 | game-performance = pkgs.writeShellApplication { |
| 21 | name = "game-performance"; |
| 22 | runtimeInputs = [ pkgs.power-profiles-daemon pkgs.systemd ]; |
| 23 | text = '' |
| 24 | if ! powerprofilesctl list | grep -q 'performance:'; then |
| 25 | exec "$@" |
| 26 | fi |
| 27 | if [ -n "''${GAME_PERFORMANCE_SCREENSAVER_ON:-}" ]; then |
| 28 | exec powerprofilesctl launch -p performance \ |
| 29 | -r "Launched with CachyOS game-performance utility" -- "$@" |
| 30 | else |
| 31 | exec systemd-inhibit --why "CachyOS game-performance is running" powerprofilesctl launch \ |
| 32 | -p performance -r "Launched with CachyOS game-performance utility" -- "$@" |
| 33 | fi |
| 34 | ''; |
| 35 | }; |
| 36 | |
| 37 | # cachyos-settings: /usr/bin/dlss-swapper. Steam launch option: |
| 38 | # dlss-swapper %command% |
| 39 | dlss-swapper = pkgs.writeShellScriptBin "dlss-swapper" '' |
| 40 | export PROTON_ENABLE_NGX_UPDATER=1 |
| 41 | export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE=on |
| 42 | export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE=on |
| 43 | export DXVK_NVAPI_DRS_NGX_DLSS_FG_OVERRIDE=on |
| 44 | export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest |
| 45 | export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest |
| 46 | exec "$@" |
| 47 | ''; |
| 48 | in |
| 49 | { |
| 50 | # --- Kernel ---------------------------------------------------------------- |
| 51 | |
| 52 | # The overlay pins the kernel to the nixpkgs it was built with. Its binary |
| 53 | # cache isn't used, so the kernel compiles locally. |
| 54 | nixpkgs.overlays = [ inputs.nix-cachyos-kernel.overlays.pinned ]; |
| 55 | |
| 56 | # Same build as linux-cachyos on CachyOS's znver4 repo: EEVDF scheduler, |
| 57 | # 1000 Hz, full preemption, Clang ThinLTO, tuned for Zen 4 (also right for |
| 58 | # this Zen 5 CPU, as on CachyOS). |
| 59 | boot.kernelPackages = pkgs.cachyosKernels.linuxPackages-cachyos-latest-lto-zen4; |
| 60 | |
| 61 | boot.kernelParams = [ |
| 62 | "nowatchdog" |
| 63 | # cachyos-settings: udev/rules.d/30-zram.rules turns zswap off once zram is |
| 64 | # up; it is always up here, so it is simply off from boot. |
| 65 | "zswap.enabled=0" |
| 66 | ]; |
| 67 | |
| 68 | # modprobe.d/blacklist.conf |
| 69 | boot.blacklistedKernelModules = [ |
| 70 | "iTCO_wdt" |
| 71 | "sp5100_tco" |
| 72 | ]; |
| 73 | |
| 74 | # modules-load.d/ntsync.conf: Wine/Proton's NT synchronization primitives. |
| 75 | boot.kernelModules = [ "ntsync" ]; |
| 76 | |
| 77 | boot.extraModprobeConfig = '' |
| 78 | # modprobe.d/nvidia.conf |
| 79 | options nvidia NVreg_InitializeSystemMemoryAllocations=0 |
| 80 | # cachyos-iw-set-regdomain: the Wi-Fi regulatory domain, from the time zone. |
| 81 | options cfg80211 ieee80211_regdom="NO" |
| 82 | ''; |
| 83 | |
| 84 | # --- sysctl.d/70-cachyos-settings.conf -------------------------------------- |
| 85 | |
| 86 | boot.kernel.sysctl = { |
| 87 | # 150 rather than the file's 100: 30-zram.rules raises it to 150 as soon as |
| 88 | # zram is active, which on CachyOS is always. |
| 89 | "vm.swappiness" = 150; |
| 90 | "vm.vfs_cache_pressure" = 50; |
| 91 | "vm.dirty_bytes" = 268435456; |
| 92 | "vm.page-cluster" = 0; |
| 93 | "vm.dirty_background_bytes" = 67108864; |
| 94 | "vm.dirty_writeback_centisecs" = 1500; |
| 95 | "kernel.nmi_watchdog" = 0; |
| 96 | "kernel.unprivileged_userns_clone" = 1; |
| 97 | "kernel.printk" = "3 3 3 3"; |
| 98 | "kernel.kptr_restrict" = 2; |
| 99 | "net.core.netdev_max_backlog" = 4096; |
| 100 | "fs.file-max" = 2097152; |
| 101 | }; |
| 102 | |
| 103 | # --- udev rules --------------------------------------------------------------- |
| 104 | |
| 105 | services.udev.extraRules = '' |
| 106 | # 60-ioschedulers.rules |
| 107 | ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq" |
| 108 | ACTION=="add|change", KERNEL=="sd[a-z]*|mmcblk[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline" |
| 109 | ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="kyber" |
| 110 | |
| 111 | # 40-hpet-permissions.rules, 99-cpu-dma-latency.rules |
| 112 | KERNEL=="rtc0", GROUP="audio" |
| 113 | KERNEL=="hpet", GROUP="audio" |
| 114 | DEVPATH=="/devices/virtual/misc/cpu_dma_latency", OWNER="root", GROUP="audio", MODE="0660" |
| 115 | |
| 116 | # 50-sata.rules |
| 117 | ACTION=="add", SUBSYSTEM=="scsi_host", KERNEL=="host*", ATTR{link_power_management_supported}=="1", ATTR{link_power_management_policy}=="*", ATTR{link_power_management_policy}="max_performance" |
| 118 | |
| 119 | # 71-nvidia.rules: runtime power management for the NVIDIA GPU. |
| 120 | ACTION=="add|bind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="auto" |
| 121 | ACTION=="remove|unbind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="on" |
| 122 | ''; |
| 123 | |
| 124 | # --- tmpfiles.d --------------------------------------------------------------- |
| 125 | |
| 126 | systemd.tmpfiles.rules = [ |
| 127 | # thp.conf, thp-shrinker.conf |
| 128 | "w! /sys/kernel/mm/transparent_hugepage/defrag - - - - defer+madvise" |
| 129 | "w! /sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none - - - - 409" |
| 130 | # coredump.conf |
| 131 | "e /var/lib/systemd/coredump - - - 3d" |
| 132 | ]; |
| 133 | |
| 134 | # --- systemd ------------------------------------------------------------------ |
| 135 | |
| 136 | # system.conf.d/00-timeout.conf, 10-limits.conf |
| 137 | systemd.settings.Manager = { |
| 138 | DefaultTimeoutStartSec = "15s"; |
| 139 | DefaultTimeoutStopSec = "10s"; |
| 140 | DefaultLimitNOFILE = "2048:2097152"; |
| 141 | }; |
| 142 | # user.conf.d/00-timeout.conf, 10-limits.conf |
| 143 | systemd.user.settings.Manager = { |
| 144 | DefaultTimeoutStartSec = "15s"; |
| 145 | DefaultTimeoutStopSec = "10s"; |
| 146 | DefaultLimitNOFILE = "1024:1048576"; |
| 147 | }; |
| 148 | |
| 149 | # journald.conf.d/00-journal-size.conf |
| 150 | services.journald.settings.Journal.SystemMaxUse = "50M"; |
| 151 | |
| 152 | # timesyncd.conf.d/10-timesyncd.conf |
| 153 | services.timesyncd = { |
| 154 | servers = [ "time.cloudflare.com" ]; |
| 155 | fallbackServers = [ |
| 156 | "time.google.com" |
| 157 | "0.nixos.pool.ntp.org" |
| 158 | "1.nixos.pool.ntp.org" |
| 159 | ]; |
| 160 | }; |
| 161 | |
| 162 | # system/user@.service.d/delegate.conf: lets user services (and ananicy) |
| 163 | # manage their own CPU, I/O and memory controllers. |
| 164 | systemd.services."user@".serviceConfig.Delegate = "cpu cpuset io memory pids"; |
| 165 | |
| 166 | # system/rtkit-daemon.service.d/override.conf |
| 167 | security.rtkit.enable = true; |
| 168 | systemd.services.rtkit-daemon.serviceConfig.LogLevelMax = "info"; |
| 169 | |
| 170 | # systemd-oomd per-slice defaults (system.slice.d, user-.slice.d, |
| 171 | # session.slice.d). |
| 172 | systemd.oomd.enable = true; |
| 173 | systemd.slices = { |
| 174 | "system".sliceConfig = { |
| 175 | ManagedOOMSwap = "kill"; |
| 176 | ManagedOOMMemoryPressure = "kill"; |
| 177 | ManagedOOMMemoryPressureLimit = "80%"; |
| 178 | }; |
| 179 | "user-".sliceConfig = { |
| 180 | ManagedOOMSwap = "kill"; |
| 181 | ManagedOOMMemoryPressure = "kill"; |
| 182 | ManagedOOMMemoryPressureLimit = "80%"; |
| 183 | }; |
| 184 | }; |
| 185 | systemd.user.slices.session.sliceConfig.ManagedOOMPreference = "omit"; |
| 186 | |
| 187 | # system/pci-latency.service + /usr/bin/pci-latency |
| 188 | systemd.services.pci-latency = { |
| 189 | description = "Adjust latency timers for PCI peripherals"; |
| 190 | wantedBy = [ "multi-user.target" ]; |
| 191 | serviceConfig.Type = "oneshot"; |
| 192 | path = [ pkgs.pciutils ]; |
| 193 | script = '' |
| 194 | setpci -v -s '*:*' latency_timer=20 |
| 195 | setpci -v -s '0:0' latency_timer=0 |
| 196 | setpci -v -d "*:*:04xx" latency_timer=80 |
| 197 | ''; |
| 198 | }; |
| 199 | |
| 200 | # security/limits.d/20-audio.conf |
| 201 | security.pam.loginLimits = [ |
| 202 | { |
| 203 | domain = "@audio"; |
| 204 | item = "rtprio"; |
| 205 | type = "-"; |
| 206 | value = "99"; |
| 207 | } |
| 208 | { |
| 209 | domain = "@audio"; |
| 210 | item = "nice"; |
| 211 | type = "-"; |
| 212 | value = "-11"; |
| 213 | } |
| 214 | ]; |
| 215 | |
| 216 | hardware.wirelessRegulatoryDatabase = true; |
| 217 | |
| 218 | # --- CachyOS services --------------------------------------------------------- |
| 219 | |
| 220 | # ananicy-cpp with CachyOS's rules: renices games, compilers, browsers etc. |
| 221 | services.ananicy = { |
| 222 | enable = true; |
| 223 | package = pkgs.ananicy-cpp; |
| 224 | rulesProvider = pkgs.ananicy-rules-cachyos; |
| 225 | }; |
| 226 | |
| 227 | # bpftune: auto-tunes network and other kernel settings (enabled on CachyOS). |
| 228 | services.bpftune.enable = true; |
| 229 | |
| 230 | # sched-ext schedulers, as CachyOS's scx-scheds. Off there too (scx_loader was |
| 231 | # disabled); to try one: |
| 232 | # services.scx = { enable = true; scheduler = "scx_lavd"; }; |
| 233 | |
| 234 | # power-profiles-daemon, with the profile you had set on CachyOS. |
| 235 | services.power-profiles-daemon.enable = true; |
| 236 | # Started with the daemon itself (wanting it from multi-user.target makes an |
| 237 | # ordering cycle). Skipped where there is no performance profile, e.g. in a VM. |
| 238 | systemd.services.power-profile-performance = { |
| 239 | description = "Select the performance power profile"; |
| 240 | after = [ "power-profiles-daemon.service" ]; |
| 241 | bindsTo = [ "power-profiles-daemon.service" ]; |
| 242 | wantedBy = [ "power-profiles-daemon.service" ]; |
| 243 | serviceConfig.Type = "oneshot"; |
| 244 | path = [ pkgs.power-profiles-daemon ]; |
| 245 | script = '' |
| 246 | if powerprofilesctl list | grep -q 'performance:'; then |
| 247 | powerprofilesctl set performance |
| 248 | fi |
| 249 | ''; |
| 250 | }; |
| 251 | |
| 252 | environment.systemPackages = [ |
| 253 | game-performance |
| 254 | dlss-swapper |
| 255 | ]; |
| 256 | } |