Статья основана на моих оригинальных англоязычных записях тут и тут.
Приветствую, несчастные владельцы ноутбуков с технологией с NVIDIA Optimus, а именно те, кому посчастливилось иметь зелёную GPU до поколения Turing (GTX 1650 и выше). Список поколений микроархитектур NVIDIA можно найти на википедии. Как известно, начиная с версии 435.xx в проприетарном драйвере появилась нормальная поддержка энергосбережения для этих архитектур, так что если у вас NVIDIA GTX 1650 и выше, всё должно работать из коробки и простыню ниже можно не читать.
Сразу оговорюсь, что решение, представленное ниже, не зависит от дистрибутива и DE и не прибито к Wayland или X11. Хотя некоторые лайфхаки могут быть специфичны для GNOME, так как он является моей основной средой рабочего стола.
Единственным требованием является использование проприетарного драйвера NVIDIA.
Дано
- Ноутбук
MSI GS63VRсGTX 1060на борту; - ОС:
Fedora 38; - Проприетарный драйвер NVIDIA (из RPMFusion);
- Основной рабочий сеанс:
GNOME Shell/Wayland.
Что хочется получить
- Полноценное отключение дискретной GPU по умолчанию, соответственно, продлевая жизнь от батареи;
- Возможность включить дискретную GPU и запустить какие-либо приложения на ней с помощью технологии
PRIME Offloadingштатными средствами DE; - Возможность переключения на сеанс, запущенный полностью на NVIDIA (без использования
PRIME) по необходимости; - Возможность выключить дискретную GPU в любое время (вернуть в состояние по умолчанию);
- Включение/отключение GPU должно происходить без перезагрузки системы, а так же без выхода из рабочей сессии;
- По возможности отслеживать запущенные процессы на GPU и мониторить показания.
Мир OpenSource-решений предлагает нам 2 стула:
- Bumblebee + bbswitch. Несмотря на то, что сам Bumblebee устарел и использование
VirtualGLпри наличииPRIME Offloadingсмысла не имеет,bbswitchпродолжает работать довольно неплохо и делает ровно то, что мне нужно. Я даже попытался использоватьbumblebeedв качестве исключительно обёртки надbbswitch(для загрузки/выгрузки модулей ядра и собственно дёргания/proc/acpi/bbswitch), сделав для этого целый bash-костыль. Несмотря на то, что это даже работает, претендовать на нормальное решение никак не может: обратная связь отсутствует, а во время работы GPU его могут «увидеть» другие приложения, которые мы напрямую не запускали через скрипт, иbumblebeedпро них не в курсе. В таком случае, GPU не сможет выключиться, когда придёт время, так как модули используются этими приложениями (пункты4и5). Второй минус - нам приходится запускать нужные приложения через скрипт, DE не предлагает нам пункта меню с запуском на дискретной GPU (пункт2). - system76-power и optimus-manager/prime-select и им подобные: такие утилиты позволяют менять конфигурацию модулей в
initramfsи конфигX11в зависимости от выбранного режима. Как можно понять, это требует иногда длительного ожидания пересборкиinitramfs, а затем перезагрузки ОС, что противоречит пунктам4и5.
Спустя около года мучений я пришёл к законченному решению, которым делюсь с вами.
Решение
Что ж, раз из коробки ничего не придумали для моих хотелок, пришлось это запилить. Так как решение с bbswitch в целом меня устраивало, не хватает лишь адекватной обёртки над ним. Вырисовывается следующая простейшая архитектура:
- bbswitchd: backend-демон, занимающийся только тем, что загружает/выгружает нужные модули, а так же пишет в
/proc/acpi/bbswitch. Эдакий Bumblebee на минималках, однако, работающий из коробки и поставляющийся с необходимыми политикамиSELinux, правиламиudevи конфигурациейmodprobe. Общение происходит через UNIX-сокет (для этого не забыть добавить пользователя в группуbbswitchd). В комплекте идёт простейшая утилитаbbswitchдля включения и выключения GPU:$ bbswitch Usage: bbswitch on | off | status $ bbswitch on # Turn discrete GPU on $ bbswitch status # Request current status ON $ bbswitch off # Turn discrete GPU off - bbswitch-gui: соответственно, графический фронтэнд на
Python+GTK3, позволяющий:- Включать и выключать питание дискретной GPU;
- Просматривать список процессов, использующих в данный момент дискретный GPU с возможностью быстро убить выбранные процессы;
- Мониторить параметры видеокарты (с помощью NVML API), а так же список загруженных модулей ядра;
- Адекватно обрабатывать ошибки при загрузке модулей и работе с
bbswitch; - Висеть в системном лотке как
AppIndicatorи не мешать.
Более детальная информация со скриншотами и примерами использования доступна в README на GitHub для обоих проектов.
Оба приложения опакечены для Fedora (Copr):
$ sudo dnf copr enable polter/bumblebee
$ sudo dnf install bbswitch-gui
И для Ubuntu (PPA):
$ sudo add-apt-repository ppa:polter-rnd/bbswitch-gui
$ sudo apt update
$ sudo apt-get install bbswitch-gui
После установки видим следующие конфигурационные файлы:
-
Запрет на автоматическую загрузку модулей
nvidia, а так же опцияload_state=0для модуляbbswitchдля выключения дискретной GPU при загрузке:$ cat /lib/modprobe.d/bbswitch.conf options bbswitch load_state=0 blacklist nvidia blacklist nvidia-drm blacklist nvidia-modeset blacklist nvidia-uvm blacklist nouveau -
Автоматическая загрузка модуля
bbswitch:$ cat /lib/modules-load.d/bbswitch.conf # Load bbswitch.ko at boot bbswitch -
Udev-правило для запрета оконному менеджеру
mutterобнаруживать дискретную GPU:$ cat /lib/udev/rules.d/60-bbswitch-nvidia-mutter.rules DRIVERS=="nvidia", SUBSYSTEM=="drm", TAG+="mutter-device-ignore" -
Udev-правило для автоматического удаления файлов устройств NVIDIA:
$ cat /lib/udev/rules.d/90-bbswitch-nvidia-dev.rules # Put this file in /lib/udev/rules.d or /etc/udev/rules.d # Prevent the nvidia card from "randomly" turning on DEVPATH=="/module/nvidia", ACTION=="remove", RUN+="/bin/sh -c 'rm -f /dev/nvidiactl /dev/nvidia[0-9] /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools'" -
А так же
systemd-сервис и соответствующий сокет:$ systemctl status bbswitchd.service ● bbswitchd.service - GPU power state management daemon Loaded: loaded (/usr/lib/systemd/system/bbswitchd.service; disabled; preset: disabled) TriggeredBy: ● bbswitchd.socket ...$ systemctl status bbswitchd.socket ● bbswitchd.socket - Socket for bbswitchd daemon Loaded: loaded (/usr/lib/systemd/system/bbswitchd.socket; enabled; preset: enabled) Active: active (running) since Fri 2023-08-11 17:54:11 MSK; 5h 17min ago Triggers: ● bbswitchd.service ...
После перезагрузки получаем максимально удобный механизм запуска приложений на внешней GPU: после включения дискретного адаптера DE начнёт предлагать запустить приложения на ней (при наличии switcheroo-control, который установлен из коробки в большинстве дистрибутивов).
Если GPU не может выключиться из-за того, что какие-либо приложения её используют, это видно в главном окне (вместе с информацией об утилизации ресурсов) и при необходимости можно их убить.
Tips & Tricks
Ниже несколько лайфхаков, которые делают использование всего этого более мягким и шелковистым.
Автозапуск bbswitch-gui
Чтобы bbswitch-gui запускался свёрнутым в трей, можно создать файл ~/.config/autostart/io.github.polter-rnd.bbswitch-gui.desktop со следующим содержимым (опция -m означает запуск в свёрнутом состоянии):
[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=BBswitch GUI
Comment=GUI tool for managing NVIDIA GPU power states and utilization
Exec=/usr/bin/bbswitch-gui -v -m
Icon=bbswitch-gui
Categories=System;Monitor;Utility;X-GNOME-Utilities;
Keywords=nvidia;bbswitch;optimus;prime;
X-AppInstall-Keywords=nvidia;bbswitch;optimus;prime;
X-GNOME-Keywords=nvidia;bbswitch;optimus;prime;
X-GNOME-UsesNotifications=true
Запуск gnome-shell всегда на встроенной видеокарте
Если выйти из Wayland-сессии со включенной NVIDIA GPU (например, чтобы зайти в X11 сессию), то при следующем входе gnome-shell увидит включенную видеокарту и подключится к ней. В результате не получится отключить дискретную GPU, т.к. процесс композитора к ней подключён.
Для решения этого переопределим конфигурацию сервиса org.gnome.Shell@wayland.service:
$ systemctl --user edit org.gnome.Shell@wayland.service
Следующим содержимым:
[Service]
ExecStartPre=/usr/bin/mkdir -p /tmp/egl_vendor.d
ExecStartPre=/usr/bin/rm -f /tmp/egl_vendor.d/10_nvidia.json
ExecStartPre=/usr/bin/ln -fs /usr/share/glvnd/egl_vendor.d/50_mesa.json /tmp/egl_vendor.d
Environment=__EGL_VENDOR_LIBRARY_DIRS=/tmp/egl_vendor.d
ExecStartPost=/usr/bin/ln -fs /usr/share/glvnd/egl_vendor.d/10_nvidia.json /tmp/egl_vendor.d
Теперь видеокарта NVIDIA скрыта от gnome-shell при запуске на Wayland, в то время, как для остальных процессов она остаётся доступна.
Если вы не пользуетесь Wayland и хотите такое же поведение для X11 сессии, переопределять нужно org.gnome.Shell@x11.service.
Переключение на сеанс, запущенный полностью на NVIDIA GPU
Чтобы реализовать пункт 3 в моем случае было проще всего настроить X11 для работы в NVIDIA-only режиме, в то время, как дефолтная Wayland-сессия всегда запускается на встроенном видеоадаптере:
$ cat /etc/X11/xorg.conf.d/10-nvidia.conf
Section "OutputClass"
Identifier "nvidia"
MatchDriver "nvidia-drm"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration"
Option "SLI" "Auto"
Option "BaseMosaic" "on"
Option "PrimaryGPU" "yes"
EndSection
Section "ServerLayout"
Identifier "layout"
Option "AllowNVIDIAGPUScreens"
EndSection
Таким образом, при входе в сеанс X11 при включенной дискретной GPU иксы запустятся полностью на NVIDIA, в тоже время, при выключенной - запустятся на встроенном графическом адаптере. Удобно (при наличии сеанса Wayland, из которого можно всегда выключить дискретный адаптер при необходимости).
Сохранение конфигурации экранов при переключении X11/Wayland
В моём случае в сеансе Wayland используется дробное масштабирование (экспериментальная возможность mutter). Однако, при запуске сеанса X11 наличие дробного масштаба в конфигурации мониторов делает её невалидной, и оконный менеджер сбрасывает её на конфигурацию по-умолчанию. Чтобы комфортно сохранять конфигурацию в отдельных файлах для X11 и Wayland, можно сделать следующее:
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.wayland
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.x11
$ ln -sf ~/.config/monitors.xml.${XDG_SESSION_TYPE} ~/.config/monitors.xml
Таким образом мы сохранили конфигурацию в отдельные файлы и выставили символьную ссылку на конфигурацию текущего сеанса.
Осталось добавить костыль в ~/.bash_profile для переключения конфигурации при входе:
if [ "${XDG_SESSION_TYPE}" = "wayland" -o "${XDG_SESSION_TYPE}" = "x11" ]
then
MONITORS_XML="${HOME}/.config/monitors.xml"
if [ -L "${MONITORS_XML}~" ]
then
cp "${MONITORS_XML}" $(readlink -f "${MONITORS_XML}~")
rm -f "${MONITORS_XML}~"
fi
ln -sf ${MONITORS_XML}{.${XDG_SESSION_TYPE},}
fi




