at 23.05-pre 18 kB view raw
1# This module provides the proprietary NVIDIA X11 / OpenGL drivers. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 nvidia_x11 = let 9 drivers = config.services.xserver.videoDrivers; 10 isDeprecated = str: (hasPrefix "nvidia" str) && (str != "nvidia"); 11 hasDeprecated = drivers: any isDeprecated drivers; 12 in if (hasDeprecated drivers) then 13 throw '' 14 Selecting an nvidia driver has been modified for NixOS 19.03. The version is now set using `hardware.nvidia.package`. 15 '' 16 else if (elem "nvidia" drivers) then cfg.package else null; 17 18 enabled = nvidia_x11 != null; 19 cfg = config.hardware.nvidia; 20 21 pCfg = cfg.prime; 22 syncCfg = pCfg.sync; 23 offloadCfg = pCfg.offload; 24 primeEnabled = syncCfg.enable || offloadCfg.enable; 25 nvidiaPersistencedEnabled = cfg.nvidiaPersistenced; 26 nvidiaSettings = cfg.nvidiaSettings; 27 busIDType = types.strMatching "([[:print:]]+[\:\@][0-9]{1,3}\:[0-9]{1,2}\:[0-9])?"; 28in 29 30{ 31 imports = 32 [ 33 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "enable" ] [ "hardware" "nvidia" "prime" "sync" "enable" ]) 34 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "sync" "allowExternalGpu" ]) 35 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "nvidiaBusId" ] [ "hardware" "nvidia" "prime" "nvidiaBusId" ]) 36 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "intelBusId" ] [ "hardware" "nvidia" "prime" "intelBusId" ]) 37 ]; 38 39 options = { 40 hardware.nvidia.powerManagement.enable = mkOption { 41 type = types.bool; 42 default = false; 43 description = lib.mdDoc '' 44 Experimental power management through systemd. For more information, see 45 the NVIDIA docs, on Chapter 21. Configuring Power Management Support. 46 ''; 47 }; 48 49 hardware.nvidia.powerManagement.finegrained = mkOption { 50 type = types.bool; 51 default = false; 52 description = lib.mdDoc '' 53 Experimental power management of PRIME offload. For more information, see 54 the NVIDIA docs, chapter 22. PCI-Express runtime power management. 55 ''; 56 }; 57 58 hardware.nvidia.modesetting.enable = mkOption { 59 type = types.bool; 60 default = false; 61 description = lib.mdDoc '' 62 Enable kernel modesetting when using the NVIDIA proprietary driver. 63 64 Enabling this fixes screen tearing when using Optimus via PRIME (see 65 {option}`hardware.nvidia.prime.sync.enable`. This is not enabled 66 by default because it is not officially supported by NVIDIA and would not 67 work with SLI. 68 ''; 69 }; 70 71 hardware.nvidia.prime.nvidiaBusId = mkOption { 72 type = busIDType; 73 default = ""; 74 example = "PCI:1:0:0"; 75 description = lib.mdDoc '' 76 Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci 77 shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0". 78 ''; 79 }; 80 81 hardware.nvidia.prime.intelBusId = mkOption { 82 type = busIDType; 83 default = ""; 84 example = "PCI:0:2:0"; 85 description = lib.mdDoc '' 86 Bus ID of the Intel GPU. You can find it using lspci; for example if lspci 87 shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0". 88 ''; 89 }; 90 91 hardware.nvidia.prime.amdgpuBusId = mkOption { 92 type = busIDType; 93 default = ""; 94 example = "PCI:4:0:0"; 95 description = lib.mdDoc '' 96 Bus ID of the AMD APU. You can find it using lspci; for example if lspci 97 shows the AMD APU at "04:00.0", set this option to "PCI:4:0:0". 98 ''; 99 }; 100 101 hardware.nvidia.prime.sync.enable = mkOption { 102 type = types.bool; 103 default = false; 104 description = lib.mdDoc '' 105 Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME. 106 If enabled, the NVIDIA GPU will be always on and used for all rendering, 107 while enabling output to displays attached only to the integrated Intel GPU 108 without a multiplexer. 109 110 Note that this option only has any effect if the "nvidia" driver is specified 111 in {option}`services.xserver.videoDrivers`, and it should preferably 112 be the only driver there. 113 114 If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be 115 specified ({option}`hardware.nvidia.prime.nvidiaBusId` and 116 {option}`hardware.nvidia.prime.intelBusId`). 117 118 If you enable this, you may want to also enable kernel modesetting for the 119 NVIDIA driver ({option}`hardware.nvidia.modesetting.enable`) in order 120 to prevent tearing. 121 122 Note that this configuration will only be successful when a display manager 123 for which the {option}`services.xserver.displayManager.setupCommands` 124 option is supported is used. 125 ''; 126 }; 127 128 hardware.nvidia.prime.sync.allowExternalGpu = mkOption { 129 type = types.bool; 130 default = false; 131 description = lib.mdDoc '' 132 Configure X to allow external NVIDIA GPUs when using optimus. 133 ''; 134 }; 135 136 hardware.nvidia.prime.offload.enable = mkOption { 137 type = types.bool; 138 default = false; 139 description = lib.mdDoc '' 140 Enable render offload support using the NVIDIA proprietary driver via PRIME. 141 142 If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be 143 specified ({option}`hardware.nvidia.prime.nvidiaBusId` and 144 {option}`hardware.nvidia.prime.intelBusId`). 145 ''; 146 }; 147 148 hardware.nvidia.nvidiaSettings = mkOption { 149 default = true; 150 type = types.bool; 151 description = lib.mdDoc '' 152 Whether to add nvidia-settings, NVIDIA's GUI configuration tool, to 153 systemPackages. 154 ''; 155 }; 156 157 hardware.nvidia.nvidiaPersistenced = mkOption { 158 default = false; 159 type = types.bool; 160 description = lib.mdDoc '' 161 Update for NVIDA GPU headless mode, i.e. nvidia-persistenced. It ensures all 162 GPUs stay awake even during headless mode. 163 ''; 164 }; 165 166 hardware.nvidia.forceFullCompositionPipeline = lib.mkOption { 167 default = false; 168 type = types.bool; 169 description = lib.mdDoc '' 170 Whether to force-enable the full composition pipeline. 171 This sometimes fixes screen tearing issues. 172 This has been reported to reduce the performance of some OpenGL applications and may produce issues in WebGL. 173 It also drastically increases the time the driver needs to clock down after load. 174 ''; 175 }; 176 177 hardware.nvidia.package = lib.mkOption { 178 type = types.package; 179 default = config.boot.kernelPackages.nvidiaPackages.stable; 180 defaultText = literalExpression "config.boot.kernelPackages.nvidiaPackages.stable"; 181 description = lib.mdDoc '' 182 The NVIDIA X11 derivation to use. 183 ''; 184 example = literalExpression "config.boot.kernelPackages.nvidiaPackages.legacy_340"; 185 }; 186 187 hardware.nvidia.open = lib.mkOption { 188 type = lib.types.bool; 189 default = false; 190 description = lib.mdDoc '' 191 Whether to use the open source kernel module 192 ''; 193 }; 194 }; 195 196 config = let 197 igpuDriver = if pCfg.intelBusId != "" then "modesetting" else "amdgpu"; 198 igpuBusId = if pCfg.intelBusId != "" then pCfg.intelBusId else pCfg.amdgpuBusId; 199 in mkIf enabled { 200 assertions = [ 201 { 202 assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == ""; 203 message = '' 204 You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor. 205 ''; 206 } 207 208 { 209 assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != ""); 210 message = '' 211 When NVIDIA PRIME is enabled, the GPU bus IDs must configured. 212 ''; 213 } 214 215 { 216 assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21"; 217 message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21."; 218 } 219 220 { 221 assertion = !(syncCfg.enable && offloadCfg.enable); 222 message = "Only one NVIDIA PRIME solution may be used at a time."; 223 } 224 225 { 226 assertion = !(syncCfg.enable && cfg.powerManagement.finegrained); 227 message = "Sync precludes powering down the NVIDIA GPU."; 228 } 229 230 { 231 assertion = cfg.powerManagement.finegrained -> offloadCfg.enable; 232 message = "Fine-grained power management requires offload to be enabled."; 233 } 234 235 { 236 assertion = cfg.powerManagement.enable -> versionAtLeast nvidia_x11.version "430.09"; 237 message = "Required files for driver based power management only exist on versions >= 430.09."; 238 } 239 240 { 241 assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware); 242 message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver"; 243 } 244 ]; 245 246 # If Optimus/PRIME is enabled, we: 247 # - Specify the configured NVIDIA GPU bus ID in the Device section for the 248 # "nvidia" driver. 249 # - Add the AllowEmptyInitialConfiguration option to the Screen section for the 250 # "nvidia" driver, in order to allow the X server to start without any outputs. 251 # - Add a separate Device section for the Intel GPU, using the "modesetting" 252 # driver and with the configured BusID. 253 # - OR add a separate Device section for the AMD APU, using the "amdgpu" 254 # driver and with the configures BusID. 255 # - Reference that Device section from the ServerLayout section as an inactive 256 # device. 257 # - Configure the display manager to run specific `xrandr` commands which will 258 # configure/enable displays connected to the Intel iGPU / AMD APU. 259 260 services.xserver.drivers = let 261 in optional primeEnabled { 262 name = igpuDriver; 263 display = offloadCfg.enable; 264 modules = optionals (igpuDriver == "amdgpu") [ pkgs.xorg.xf86videoamdgpu ]; 265 deviceSection = '' 266 BusID "${igpuBusId}" 267 ${optionalString (syncCfg.enable && igpuDriver != "amdgpu") ''Option "AccelMethod" "none"''} 268 ''; 269 } ++ singleton { 270 name = "nvidia"; 271 modules = [ nvidia_x11.bin ]; 272 display = !offloadCfg.enable; 273 deviceSection = optionalString primeEnabled 274 '' 275 BusID "${pCfg.nvidiaBusId}" 276 ${optionalString syncCfg.allowExternalGpu "Option \"AllowExternalGpus\""} 277 ''; 278 screenSection = 279 '' 280 Option "RandRRotation" "on" 281 '' + optionalString syncCfg.enable '' 282 Option "AllowEmptyInitialConfiguration" 283 '' + optionalString cfg.forceFullCompositionPipeline '' 284 Option "metamodes" "nvidia-auto-select +0+0 {ForceFullCompositionPipeline=On}" 285 Option "AllowIndirectGLXProtocol" "off" 286 Option "TripleBuffer" "on" 287 '' 288 ; 289 }; 290 291 services.xserver.serverLayoutSection = optionalString syncCfg.enable '' 292 Inactive "Device-${igpuDriver}[0]" 293 '' + optionalString offloadCfg.enable '' 294 Option "AllowNVIDIAGPUScreens" 295 ''; 296 297 services.xserver.displayManager.setupCommands = let 298 sinkGpuProviderName = if igpuDriver == "amdgpu" then 299 # find the name of the provider if amdgpu 300 "`${pkgs.xorg.xrandr}/bin/xrandr --listproviders | ${pkgs.gnugrep}/bin/grep -i AMD | ${pkgs.gnused}/bin/sed -n 's/^.*name://p'`" 301 else 302 igpuDriver; 303 in optionalString syncCfg.enable '' 304 # Added by nvidia configuration module for Optimus/PRIME. 305 ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource "${sinkGpuProviderName}" NVIDIA-0 306 ${pkgs.xorg.xrandr}/bin/xrandr --auto 307 ''; 308 309 environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles { 310 source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc"; 311 }; 312 313 # 'nvidia_x11' installs it's files to /run/opengl-driver/... 314 environment.etc."egl/egl_external_platform.d".source = 315 "/run/opengl-driver/share/egl/egl_external_platform.d/"; 316 317 hardware.opengl.extraPackages = [ 318 nvidia_x11.out 319 pkgs.nvidia-vaapi-driver 320 ]; 321 hardware.opengl.extraPackages32 = [ 322 nvidia_x11.lib32 323 pkgs.pkgsi686Linux.nvidia-vaapi-driver 324 ]; 325 326 environment.systemPackages = [ nvidia_x11.bin ] 327 ++ optionals cfg.nvidiaSettings [ nvidia_x11.settings ] 328 ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ]; 329 330 systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out; 331 332 systemd.services = let 333 baseNvidiaService = state: { 334 description = "NVIDIA system ${state} actions"; 335 336 path = with pkgs; [ kbd ]; 337 serviceConfig = { 338 Type = "oneshot"; 339 ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'"; 340 }; 341 }; 342 343 nvidiaService = sleepState: (baseNvidiaService sleepState) // { 344 before = [ "systemd-${sleepState}.service" ]; 345 requiredBy = [ "systemd-${sleepState}.service" ]; 346 }; 347 348 services = (builtins.listToAttrs (map (t: nameValuePair "nvidia-${t}" (nvidiaService t)) ["hibernate" "suspend"])) 349 // { 350 nvidia-resume = (baseNvidiaService "resume") // { 351 after = [ "systemd-suspend.service" "systemd-hibernate.service" ]; 352 requiredBy = [ "systemd-suspend.service" "systemd-hibernate.service" ]; 353 }; 354 }; 355 in optionalAttrs cfg.powerManagement.enable services 356 // optionalAttrs nvidiaPersistencedEnabled { 357 "nvidia-persistenced" = mkIf nvidiaPersistencedEnabled { 358 description = "NVIDIA Persistence Daemon"; 359 wantedBy = [ "multi-user.target" ]; 360 serviceConfig = { 361 Type = "forking"; 362 Restart = "always"; 363 PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid"; 364 ExecStart = "${nvidia_x11.persistenced}/bin/nvidia-persistenced --verbose"; 365 ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced"; 366 }; 367 }; 368 }; 369 370 systemd.tmpfiles.rules = optional config.virtualisation.docker.enableNvidia 371 "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin" 372 ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia) 373 "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced"; 374 375 boot.extraModulePackages = if cfg.open then [ nvidia_x11.open ] else [ nvidia_x11.bin ]; 376 hardware.firmware = lib.optional cfg.open nvidia_x11.firmware; 377 378 # nvidia-uvm is required by CUDA applications. 379 boot.kernelModules = [ "nvidia-uvm" ] ++ 380 optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ]; 381 382 # If requested enable modesetting via kernel parameter. 383 boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1" 384 ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1" 385 ++ optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1"; 386 387 services.udev.extraRules = 388 '' 389 # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded. 390 KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 255'" 391 KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \ -f 4); do mknod -m 666 /dev/nvidia$${i} c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) $${i}; done'" 392 KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 254'" 393 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'" 394 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 1'" 395 '' + optionalString cfg.powerManagement.finegrained ( 396 optionalString (versionOlder config.boot.kernelPackages.kernel.version "5.5") '' 397 # Remove NVIDIA USB xHCI Host Controller devices, if present 398 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1" 399 400 # Remove NVIDIA USB Type-C UCSI devices, if present 401 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1" 402 403 # Remove NVIDIA Audio devices, if present 404 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1" 405 '' + '' 406 # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind 407 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto" 408 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto" 409 410 # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind 411 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on" 412 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on" 413 ''); 414 415 boot.extraModprobeConfig = mkIf cfg.powerManagement.finegrained '' 416 options nvidia "NVreg_DynamicPowerManagement=0x02" 417 ''; 418 419 boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ]; 420 421 services.acpid.enable = true; 422 423 }; 424 425}