外部 GPU
在配备有 Thunderbolt 3+ 或 USB4 的计算机上,可以使用 GPU 外接盒连接桌面级外部显卡 (eGPU)。eGPU.io 是一个包含购买指南和社区论坛的优质资源。虽然大多数运行模式需要一些手动配置(如下所示),但 Linux 对 eGPU 的支持通常良好。
安装
Thunderbolt
插入后,eGPU 外接盒的 Thunderbolt 设备可能需要先进行授权(取决于您的 BIOS/UEFI 固件配置)。请参考 Thunderbolt#User device authorization。如果成功,外部显卡应出现在 lspci 中。
$ lspci -d ::03xx
00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) # internal GPU 1a:10.3 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050] (rev a1) # external GPU
根据您的计算机、其固件和外接盒固件的不同,由于 PCIe 通道数量和 OPI 模式 的限制,Thunderbolt 会在一定程度上限制主机 <-> eGPU 的带宽。
# dmesg | grep PCIe
[19888.928225] pci 0000:1a:10.3: 8.000 Gb/s available PCIe bandwidth, limited by 2.5 GT/s PCIe x4 link at 0000:05:01.0 (capable of 126.016 Gb/s with 8.0 GT/s PCIe x16 link)
您可能需要通过添加以下 内核参数 来使您的系统支持热插拔
pcie_ports=native pci=assign-busses,hpbussize=0x33,realloc,hpmmiosize=128M,hpmmioprefsize=16G
这会告知内核您的 PCIe 是可热插拔的,这是 Thunderbolt 所必需的。
如果您的 Thunderbolt GPU 使用与内部 GPU(无论是独立显卡还是集成显卡)相同的驱动程序,您可能还需要添加一条 modprobe 规则,以确保驱动程序在 thunderbolt 之后加载。
下面的示例适用于 amdgpu。创建
/etc/modprobe.d/amdgpu.conf
softdep amdgpu pre: thunderbolt
如果您没有使用 AMD GPU,请将 amdgpu 替换为您选择的驱动程序。
您可能还需要通过修改 mkinitcpio.conf 中的 MODULES=() 行,告知 mkinitcpio 在执行任何 hook 之前加载 thunderbolt 驱动程序。
MODULES=(thunderbolt)
之后,重新生成 initramfs 并重启。
这确保了 amdgpu 驱动程序仅在 thunderbolt 驱动程序初始化子系统并使外部 GPU 可用之后才加载,从而允许其正确初始化两个 GPU。
驱动程序
应安装与您的 GPU 型号兼容的驱动程序
如果安装成功,lspci -k 应显示驱动程序已与您的显卡关联
$ lspci -k -d ::03xx
1a:10.3 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050] (rev a1)
Subsystem: NVIDIA Corporation GP107 [GeForce GTX 1050]
Kernel driver in use: nvidia
Kernel modules: nouveau, nvidia_drm, nvidia
AMDGPU
请注意,AMDGPU 驱动程序(无论是通过 Thunderbolt 还是 USB4)在某些情况下可能会设置错误的 pcie_gen_cap 选项并回退到 PCIe gen 1.1 速度,这可能会导致严重的性能问题。在这种情况下,可以通过模块选项设置正确的值(参见 Kernel module#Using modprobe.d)或者直接作为 内核参数 传递。
/etc/modprobe.d/amd-egpu-pcie-speed.conf
options amdgpu pcie_gen_cap=0x40000
这将设置 PCIe gen 3 速度。完整选项列表可在 amd_pcie.h 中找到。
NVIDIA
对于某些系统上的 NVIDIA eGPU,您可能需要提前加载 thunderbolt 内核模块,以确保它在 nvidia_drm 之前加载。
- 如果您使用 mkinitcpio initramfs,请参考 mkinitcpio#MODULES 来添加模块。
- 如果您使用 Booster,请参考 Booster#Early module loading。
- 如果您使用 dracut,请参考 dracut#Early kernel module loading。
仅计算工作负载
在完成 安装步骤 后,不需要显示内容的仅计算工作负载,如 通用 GPU 计算#CUDA,应该无需额外配置即可运行。nvidia-smi 工具(由 nvidia-utils 软件包提供)应配合专有 NVIDIA 驱动程序正常工作。专有 NVENC/NVDEC 也应正常工作(不含 OpenGL 互操作)。
此用例还应支持完整的热插拔。热拔出也应该是可能的(可能取决于所使用的驱动程序)。在 NVIDIA 上,激活的 nvidia-persistenced 可能会阻止干净的热拔出。
Xorg
可以采用多种结合内部 (iGPU) 和外部 (eGPU) 显卡的设置,每种设置各有优缺点。
Xorg 在 eGPU 上渲染,PRIME 显示卸载至 iGPU
- 大多数使用 GPU 的程序在 eGPU 上开箱即用:
glxinfo/glxgears,eglinfo/eglgears_x11,NVENC/NVDEC(包括 OpenGL 互操作)。 - Xorg 仅在插入 eGPU 时启动。
- 连接到 eGPU 的显示器开箱即用,连接到 iGPU 的显示器(即笔记本内置屏幕)可以使用 PRIME 显示 (display) 卸载。
主文章见 PRIME#Reverse PRIME。NVIDIA 驱动文档中也有记录:第 34 章. 使用 RandR 1.4 卸载图形显示。
使用如下 Xorg 配置片段
/etc/X11/xorg.conf.d/80-egpu-primary-igpu-offload.conf
Section "Device"
Identifier "Device0"
Driver "nvidia"
BusID "PCI:26:16:3" # Edit according to lspci, translate from hex to decimal.
Option "AllowExternalGpus" "True" # Required for proprietary NVIDIA driver.
EndSection
Section "Module"
# Load modesetting module for the iGPU, which should show up in XrandR 1.4 as a provider.
Load "modesetting"
EndSection
xorg.conf 片段中,我们需要将 1a:10.3 转换为 26:16:3。ServerLayout 和 Screen 部分,因为这些会自动推断。第一个定义的 Device 将被视为主设备。要验证此设置,请使用 xrandr --listproviders,它应显示
Providers: number : 2 Provider 0: id: 0x1b8 cap: 0x1, Source Output crtcs: 4 outputs: 4 associated providers: 0 name:NVIDIA-0 Provider 1: id: 0x1f3 cap: 0xf, Source Output, Sink Output, Source Offload, Sink Offload crtcs: 3 outputs: 5 associated providers: 0 name:modesetting
要输出到笔记本内置屏幕和/或连接到 iGPU 的其他显示器,可以使用 RandR 1.4 PRIME 显示 (display) 卸载,使用上述 xrandr --listproviders 输出中的名称
xrandr --setprovideroutputsource modesetting NVIDIA-0 && xrandr --auto
xrandr --auto 是可选的,可以用任何基于 RandR 的显示配置工具替代。它的存在可以防止出现所有屏幕全黑的情况。您可能希望在显示管理器显示登录提示或桌面环境启动之前运行此命令,请参阅 xrandr#Configuration 和 xinit。
Vulkan 可能会独立于 Xorg 枚举 GPU,因此在这种设置下运行例如 vkcube 时,可能需要传递 --gpu_number 1 选项。或者,可以通过激活一个在枚举期间重新排列 GPU 顺序的层来实现相同的效果:__NV_PRIME_RENDER_OFFLOAD=1 vkcube 或等效的 prime-run vkcube。
/etc/optimus-manager/xorg/ 中对应模式和显卡的文件中添加 eGPU 的 BusId 来在 eGPU 上渲染。Xorg 在 iGPU 上渲染,PRIME 渲染卸载至 eGPU
- 程序默认在 iGPU 上渲染,但可以使用 PRIME 渲染 (render) 卸载在 eGPU 上渲染它们。
- 即使 eGPU 未连接,Xorg 也会启动,但在重启之前,渲染/显示卸载将无法工作。
- 连接到 iGPU 的显示器(即笔记本内置屏幕)开箱即用,连接到 eGPU 的显示器可以使用 PRIME 显示 (display) 卸载。
主文章见 PRIME#PRIME GPU offloading。NVIDIA 驱动文档中也有记录:第 35 章. PRIME 渲染卸载。
对于许多独立 GPU 驱动程序,此模式在无需手动 Xorg 配置的情况下应为默认模式。如果不起作用,或者您使用专有 NVIDIA 驱动程序,请使用以下设置
/etc/X11/xorg.conf.d/80-igpu-primary-egpu-offload.conf
Section "Device"
Identifier "Device0"
Driver "modesetting"
EndSection
Section "Device"
Identifier "Device1"
Driver "nvidia"
BusID "PCI:26:16:3" # Edit according to lspci, translate from hex to decimal.
Option "AllowExternalGpus" "True" # Required for proprietary NVIDIA driver.
EndSection
要验证此设置,请使用 xrandr --listproviders,它应显示
$ xrandr --listproviders
Providers: number : 2 Provider 0: id: 0x47 cap: 0xf, Source Output, Sink Output, Source Offload, Sink Offload crtcs: 3 outputs: 5 associated providers: 0 name:modesetting Provider 1: id: 0x24a cap: 0x2, Sink Output crtcs: 4 outputs: 4 associated providers: 0 name:NVIDIA-G0
要在 eGPU 上渲染 some_program,可以使用 PRIME 渲染 (render) 卸载
- 适用于专有 NVIDIA 驱动程序
$ __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia some_program
- 适用于专有 NVIDIA 驱动程序(便捷包装脚本)
$ prime-run some_program
- 适用于开源驱动程序
$ DRI_PRIME=1 some_program
要输出到连接到 eGPU 的显示器,可以再次使用 RandR 1.4 PRIME 显示 (display) 卸载
$ xrandr --setprovideroutputsource NVIDIA-G0 modesetting && xrandr --auto
NVIDIA 驱动程序 460.27.04+ 为渲染和显示同时卸载的特殊情况实现了一项优化
- 增加了对 “Reverse PRIME Bypass” 的支持,这是一项在渲染卸载应用程序全屏、无重定向且仅在给定的 NVIDIA 驱动 PRIME 显示卸载输出上可见的情况下,绕过 PRIME 渲染卸载和 PRIME 显示卸载带宽开销的优化。当 X 服务器启用详细日志记录时,该优化的使用情况会在 X 日志中报告。
为 eGPU 创建独立的 Xorg 实例
主文章见 nvidia-xrun#External GPU setup。
Xorg 上 eGPU 的已知问题
- 大多数独立 GPU Xorg 驱动程序不支持热插拔:Xorg 启动时需要插入 eGPU。注销并重新登录应足以重启 Xorg。
- 完全不支持热拔出:这样做会导致系统不稳定或直接冻结(正如 NVIDIA 文档中所承认的)。
Wayland
Wayland 对 eGPU(或通常的多 GPU)的支持测试较少,但应该可以使用更少的手动配置来工作。
请注意,Wayland 合成器需要有明确的 GPU 热插拔支持,但大多数合成器已经具备一定水平的支持
- KDE 的 kwin: https://invent.kde.org/plasma/kwin/-/merge_requests/811
- GNOME 的 Mutter: https://gitlab.gnome.org/GNOME/mutter/-/issues/17, https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1562
- wlroots: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/1278
对于开源驱动程序,DRI 卸载运行良好
$ DRI_PRIME=1 some_program
一些项目(如 all-ways-egpu)正尝试为 Wayland 下的 GPU 选择提供更高效的方法。
热插拔 NVIDIA eGPU
在使用 Wayland 时可以热插拔 eGPU,并使用 PRIME 功能。NVIDIA 已经为 dGPU 实现了出色的 PRIME,对于 eGPU 也是以同样的方式工作。
首先,您需要确保没有程序在使用 NVIDIA 模块。EGL 程序即使运行在 iGPU 上,通常每个程序也会占用 1MB 的 dGPU 内存,这可以在 nvidia-smi 中看到。为了避免这种情况,请将 __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json 添加为 环境变量。最佳放置位置是 /etc/environment.d/50_mesa.conf。
然后,卸载 NVIDIA 模块
# rmmod nvidia_uvm # rmmod nvidia_drm # rmmod nvidia_modeset # rmmod nvidia
当 NVIDIA 模块不再加载时,您可以连接外部 GPU。GPU 初始化后,使用 modprobe nvidia-drm 或 modprobe nvidia-current-drm 命令重新加载 NVIDIA 模块。名称取决于模块的来源,是来自 NVIDIA 网站还是来自包管理器。在某些情况下(例如,对于 GIGABYTE AORUS GAMING BOX),eGPU 在使用专有模块时无法工作,因此您可能需要加载开源模块:modprobe nvidia-current-open-drm。
模块成功加载后,prime 功能将起作用,但由于我们将 __EGL_VENDOR_LIBRARY_FILENAMES 变量设置为使用 MESA,因此在启动程序之前,我们需要添加 __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json。完整字符串如下
__GLX_VENDOR_LIBRARY_NAME=nvidia __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/10_nvidia.json %command%
对于 GNOME 用户,您可能需要补丁 switcheroo-control,将 __EGL_VENDOR_LIBRARY_FILENAMES 包含在环境变量列表中。这将允许程序通过右键点击并选择 “使用独立显卡启动” 自然地在 eGPU 上运行。但这超出了本文的讨论范围。
热插拔 NVIDIA eGPU 并临时禁用 NVIDIA dGPU
如果您拥有 iGPU、NVIDIA dGPU 并想连接 NVIDIA eGPU,您会遇到冲突,无论怎么操作,图形渲染仅在 dGPU 上运行。为了解决这个问题,您需要临时禁用 dGPU,这样 NVIDIA 驱动程序就不会察觉到它。最好的方法是覆盖其驱动程序。
首先,您需要卸载 NVIDIA 驱动程序
# rmmod nvidia_uvm # rmmod nvidia_drm # rmmod nvidia_modeset # rmmod nvidia
然后,您需要使用 driverctlAUR 工具覆盖 dGPU 驱动程序。在本例中,0000:01:00.0 是 dGPU 的地址。可以使用 lspci 工具找到它。
# driverctl --nosave set-override 0000:01:00.0 vfio-pci
使用 --nosave 参数非常重要,以防止 driverctl 在启动时覆盖驱动程序。这在出现问题时很有用,简单的重启即可清除所有设置。
当 dGPU 被禁用时,您可以使用 modprobe nvidia-drm 加载内核模块,然后检查 nvidia-smi 显示的是 1 个还是 2 个 GPU。
恢复 dGPU 比较复杂,因为操作不够直观。首先,卸载 NVIDIA 模块,拔掉 eGPU,然后运行这一系列命令
# modprobe nvidia-current # driverctl --nosave unset-override 0000:01:00.0 # modprobe nvidia-current # driverctl --nosave unset-override 0000:01:00.0 # modprobe nvidia-current-modeset # modprobe nvidia-current-drm
奇怪的是,我们需要将前两条命令运行两次,否则无法恢复 dGPU。命令会报错一次,但这并不关键。