Load gpu_unlock before nvidia on Bazzite for an RTX 3090
Contents
This installs the gpu_unlock kernel module on Bazzite so that it runs from the initramfs, before nvidia binds the card, and leaves an RTX 3090 with full-rate FP16/BF16 (FP32-accumulate) and TF32 tensor math on every boot that carries the kernel option gpu_unlock.targets=all.
- Tested on
- Bazzite 44.20261023 (
bazzite-nvidia-openimage, kernel 7.2.9-ogc3.1.fc44), NVIDIA open driver 615.78.08, RTX 3090 (10de:2204) with VBIOS 94.02.26.80.8C, Secure Boot off; gpu-unlock at upstream commitaf28cbb, NVIDIA firmware pin 610.43.03 (2026-10-26). - Not verified
- the redo after an image update brings a new kernel (none has arrived yet); the file-removal half of Undo; the login check on a real failed boot (tested with its override variables only); Secure Boot; any card other than this one RTX 3090; the boot-menu keys in Recovery come from Bazzite's docs.
- Needs
- a login user that can read the kernel log with
journalctl -k(on the tested box: a member ofwheel); an rpm-ostree image that putsnvidiain its initramfs (Bazzite'snvidiaandnvidia-openimages do); a GA102/GA103/GA104/GA106 GeForce card from the upstream list; sudo; about 340 MB of downloads; a keyboard on the box to reach the hidden boot menu. - Time
- 30 min (plus one full power off).
- Risk
- if the unlock fails partway, the card can stay unusable until a full power off; on a single-GPU box that is a black screen, recovered from the boot menu (Recovery below). Reversible: the option lives in one deployment, the speed selectors do not survive a power off. Step 11 (optional) removes the rollback entry for good.
The steps replace upstream's make install (DKMS plus packages), which cannot work on rpm-ostree: /usr is read-only, and the image's prebuilt initramfs loads nvidia before anything on the root file system runs. Here the module, its firmware and a loader script live in /etc, rpm-ostree initramfs-etc adds them to the initramfs as an overlay, and a modprobe.d install rule runs the loader before every load of nvidia.
Before you start#
Secure Boot must be off: this path does not sign the module.
$ mokutil --sb-state SecureBoot disabledExpected:
SecureBoot disabled.The headers for the booted kernel must be in the image (Bazzite ships them as
kernel-devel-matched):$ uname -r; ls -d /usr/src/kernels/$(uname -r) 7.2.9-ogc3.1.fc44.x86_64 /usr/src/kernels/7.2.9-ogc3.1.fc44.x86_64Expected: the directory exists and its name equals
uname -r.Firmware generation needs Python 3.14 or newer:
$ python3 --version Python 3.14.8Check that the image forces
nvidiainto the initramfs. This is why the module has to be in the initramfs too:$ cat /usr/lib/dracut/dracut.conf.d/99-nvidia.conf # Omit the i915 amdgpu nvidia driver from the ramdisk, to avoid needing to regenerate # the ramdisk on updates. force_drivers+=" i915 amdgpu nvidia nvidia_drm nvidia_modeset nvidia_uvm "Note the deployments.
rpm-ostree initramfs-etcandrpm-ostree kargsbelow each build a new deployment on top of the newest one, including an update that is already staged:$ rpm-ostree status | grep -E 'ostree-image|Version:' ● ostree-image-signed:docker://ghcr.io/ublue-os/bazzite-nvidia-open:stable Version: 44.20261023 (2026-10-23T16:36:04Z)Expected: one line with
●(the booted deployment). A deployment listed above it is staged and is what you will boot next.GotchaA staged update with another kernel
The module is built for the kernel you are running. If a staged deployment brings a different kernel, the unlock silently does nothing after the power off. Boot the staged deployment first, then start at step 1.
Backup: none to take by hand. The deployment you are booted into stays as the rollback entry until you run step 11. Record the current options, to compare by eye after an Undo (the
ostree=entry names the deployment and will differ):rpm-ostree kargs > ~/kargs-before.txt
Steps#
Clone upstream and check out the pinned commit.
$ cd ~ && git clone -q https://github.com/Zagitta/gpu-unlock.git && cd gpu-unlock && git checkout -q af28cbb && git log -1 --format='%h %ad' --date=short af28cbb 2026-10-24Expected:
af28cbb 2026-10-24. The remaining commands run in~/gpu-unlock.GotchaDo not build a 3090 unlock from a newer upstream without checking
Upstream commit
fc83321broke the unlock on several RTX 3090s (issue #20,poll timeoutthen-110; issue #23).4189b17reverted it together with291f7fa, back to the tree ofc274e74, andaf28cbbhas that samekernel/code. On 2026-10-26 #20 was still open andmainwas nine commits pastaf28cbb, including a kernel refactor ("Refactor driver to be recipe driven"). Before you build from a later commit, read #20 and the newer 3090 reports.Build the module against the booted kernel.
$ make -C kernel/gpu_unlock KCFLAGS=-Werror make: Entering directory '/var/home/user/gpu-unlock/kernel/gpu_unlock' make -C "/lib/modules/7.2.9-ogc3.1.fc44.x86_64/build" M="/var/home/user/gpu-unlock/kernel/gpu_unlock" modules make[1]: Entering directory '/usr/src/kernels/7.2.9-ogc3.1.fc44.x86_64' make[2]: Entering directory '/var/home/user/gpu-unlock/kernel/gpu_unlock' warning: pahole version differs from the one used to build the kernel The kernel was built with: 131 You are using: 0 CC [M] main.o CC [M] gfw_eval.o CC [M] prep_610.o CC [M] layout_610.o CC [M] vbios_610.o CC [M] unlock_610.o LD [M] gpu_unlock.o MODPOST Module.symvers CC [M] gpu_unlock.mod.o CC [M] .module-common.o LD [M] gpu_unlock.ko BTF [M] gpu_unlock.ko Skipping BTF generation for gpu_unlock.ko due to unavailability of vmlinux make[2]: Leaving directory '/var/home/user/gpu-unlock/kernel/gpu_unlock' make[1]: Leaving directory '/usr/src/kernels/7.2.9-ogc3.1.fc44.x86_64' make: Leaving directory '/var/home/user/gpu-unlock/kernel/gpu_unlock'Expected: exit code 0 and
LD [M] gpu_unlock.ko. Thepaholewarning andSkipping BTF generationare not errors. Check the build kernel:$ modinfo -F vermagic kernel/gpu_unlock/gpu_unlock.ko 7.2.9-ogc3.1.fc44.x86_64 SMP preempt mod_unloadExpected: the first word equals
uname -rfrom Before you start.Download the pinned NVIDIA inputs and generate the firmware.
$ make assets python3 firmware/fetch_artifacts.py /home/user/.cache/gpu-unlock/inputs /home/user/.cache/gpu-unlock/inputs/NVIDIA-kernel-module-source-610.43.03.tar.xz /home/user/.cache/gpu-unlock/inputs/nvidia-utils-610.43.03-1-x86_64_v3.pkg.tar.zst make firmware make[1]: Entering directory '/var/home/user/gpu-unlock' rm -rf /home/user/.cache/gpu-unlock/assets.src mkdir -p /home/user/.cache/gpu-unlock/assets.src tar -xf /home/user/.cache/gpu-unlock/inputs/NVIDIA-kernel-module-source-610.43.03.tar.xz -C /home/user/.cache/gpu-unlock/assets.src python3 firmware/generate_v67.py "$(realpath /home/user/.cache/gpu-unlock/inputs/nvidia-utils-610.43.03-1-x86_64_v3.pkg.tar.zst)" /home/user/.cache/gpu-unlock/assets python3 firmware/extract_boot_assets.py /home/user/.cache/gpu-unlock/assets.src/NVIDIA-kernel-module-source-610.43.03 /home/user/.cache/gpu-unlock/assets make[1]: Leaving directory '/var/home/user/gpu-unlock'$ ls -l ~/.cache/gpu-unlock/assets total 82480 -rw-r--r--. 1 user user 61256 Oct 26 21:41 booter_load_ga102.bin -rw-r--r--. 1 user user 84 Oct 26 21:41 gsp_boot_desc_ga102.bin -rw-r--r--. 1 user user 24576 Oct 26 21:41 gsp_boot_ga102.bin -rw-r--r--. 1 user user 84277400 Oct 26 21:41 gsp_ga10x.bin -rw-r--r--. 1 user user 22653 Oct 26 21:41 NVIDIA-LICENSE -rw-r--r--. 1 user user 64000 Oct 26 21:41 v67-ga102-610.43.03.binExpected: the five
.binfiles andNVIDIA-LICENSE. The generators check every input and output against a pinned SHA-256 and stop on a mismatch.Save the loader script and the
modprobe.drule. Both are GPL-2.0-only, written for this setup; they are not in upstream.load.shruns before everynvidiaload, in the initramfs and later. It reads thegpu_unlock.*options from the kernel command line (insmoddoes not, unlikemodprobe), points the firmware loader at/etc/gpu-unlock/firmware, loads the module, and always exits 0 so thatnvidialoads whatever happened. It uses only shell builtins and absolute paths: the initramfs need not havecat, andPATHmay not be set there.mkdir -p bazzite cat > bazzite/load.sh <<'EOF' #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only # Run by the modprobe.d install rule for nvidia, in the initramfs and on the # root filesystem: load gpu_unlock from /etc just before nvidia. Bazzite's # /usr is read-only, so the module and its firmware live in /etc/gpu-unlock # and reach the initramfs through rpm-ostree initramfs-etc. Never fails, so # nvidia always loads. dir=/etc/gpu-unlock [ -d /sys/module/gpu_unlock ] && exit 0 # insmod does not read gpu_unlock.* from the command line as modprobe would. # Builtins only: the initramfs need not have cat or PATH set. read -r cmdline < /proc/cmdline params= for word in $cmdline; do case "$word" in gpu_unlock.*=*) params="$params ${word#gpu_unlock.}" ;; esac done [ -n "$params" ] || exit 0 # The firmware is under /etc too; resume loads it again, so the path stays set. fw=/sys/module/firmware_class/parameters/path current= [ -w "$fw" ] && read -r current < "$fw" if [ -w "$fw" ] && [ -z "$current" ]; then printf %s "$dir/firmware" > "$fw" fi /usr/bin/insmod "$dir/gpu_unlock.ko" $params exit 0 EOFThe rule runs
load.shthrough/bin/sh -c, then loadsnvidiawith its usual options. A path in it that does not exist in the initramfs stopsnvidiafrom loading at all.cat > bazzite/modprobe.conf <<'EOF' # SPDX-License-Identifier: GPL-2.0-only # Bazzite: gpu_unlock is not under /usr/lib/modules, so no softdep can name it. install nvidia /bin/sh /etc/gpu-unlock/load.sh; /sbin/modprobe --ignore-install nvidia $CMDLINE_OPTS EOFCheck what you saved:
$ sha256sum bazzite/load.sh bazzite/modprobe.conf 453dfef73e601ecbea06f43eff208fc07d633452fe8d5fdb03b81789c12801ba bazzite/load.sh fee0e6f424735d531eeb8a1c05cea70c6329f874e2159f9d1796913b20ab44b9 bazzite/modprobe.confExpected: these two hashes. A different hash means the paste changed the file.
Install the module, firmware, loader and rule into
/etc.sudo install -Dm644 kernel/gpu_unlock/gpu_unlock.ko /etc/gpu-unlock/gpu_unlock.ko sudo install -Dm755 bazzite/load.sh /etc/gpu-unlock/load.sh sudo install -Dm644 -t /etc/gpu-unlock/firmware/gpu-unlock/610.43.03 \ ~/.cache/gpu-unlock/assets/booter_load_ga102.bin \ ~/.cache/gpu-unlock/assets/gsp_ga10x.bin \ ~/.cache/gpu-unlock/assets/gsp_boot_ga102.bin \ ~/.cache/gpu-unlock/assets/gsp_boot_desc_ga102.bin \ ~/.cache/gpu-unlock/assets/v67-ga102-610.43.03.bin sudo install -Dm644 bazzite/modprobe.conf /etc/modprobe.d/gpu-unlock.confExpected: no output.
cmp kernel/gpu_unlock/gpu_unlock.ko /etc/gpu-unlock/gpu_unlock.koprints nothing.Add the files to the initramfs as an overlay.
F=/etc/gpu-unlock/firmware/gpu-unlock/610.43.03 sudo rpm-ostree initramfs-etc --force-sync \ --track=/etc/gpu-unlock/gpu_unlock.ko --track=/etc/gpu-unlock/load.sh \ --track=/etc/modprobe.d/gpu-unlock.conf \ --track=$F/booter_load_ga102.bin --track=$F/gsp_ga10x.bin --track=$F/gsp_boot_ga102.bin \ --track=$F/gsp_boot_desc_ga102.bin --track=$F/v67-ga102-610.43.03.binOutput on the tested box (same command, run as root; repository lines cut):
Checking out tree 278edb6...done … Resolving dependencies...done Checking out packages...done Running systemd-sysusers...done Running pre scripts...done Running post scripts...done Running posttrans scripts...done Writing rpmdb...done Writing OSTree commit...done Staging deployment...done Freed: 138.7 MB (pkgcache branches: 0) Run "systemctl reboot" to start a rebootExpected:
Staging deployment...done. No dracut run: the image's initramfs stays as it is and the overlay is a secondinitrdin the new boot entry.Opt in on the kernel command line.
sudo rpm-ostree kargs --append-if-missing=gpu_unlock.targets=allOutput on the tested box, cut (an image update was staged, so it listed that update's package diff):
Checking out tree 278edb6...done Resolving dependencies...done Checking out packages...done Running systemd-sysusers...done Running pre scripts...done Running post scripts...done Running posttrans scripts...done Writing rpmdb...done Writing OSTree commit...done Staging deployment...done Freed: 138.7 MB (pkgcache branches: 0) … Changes queued for next boot. Run "systemctl reboot" to start a rebootExpected:
Changes queued for next boot.NoteOptions are per deployment
rpm-ostree kargswrites the option only into the new deployment. The entry you booted from keeps its old options and is now the rollback: booting it gives a locked card. Image updates copy the option and the overlay from the deployment they build on.Power off fully, then power on.
systemctl poweroffDo not use
rebootfor the first unlock. The module refuses a card that a driver has already used since power on (card already initialised since power on (WPR2 enabled); power off first). A full power off clears that state. I saw a warm reboot clear it once (Field notes); that is not a guarantee.WarningSingle-GPU box: a failed unlock is a black screen
The screen on the card goes blank from the card's reset until
nvidia-drmtakes over. If the card's firmware does not stop during the unlock, the module keeps the DMA buffers that firmware may still use until the next power off, on purpose. Separately, with the brokenfc83321, issue #23 reports thatnvidiacould not use either card after a failed unlock until the machine was fully restarted. On a single-GPU box that means no picture: follow Recovery below.Check that this boot unlocked the card.
$ journalctl -k -b -o cat | grep 'gpu_unlock: unlocked' pci 0000:10:00.0: gpu_unlock: unlockedExpected: one line ending in
gpu_unlock: unlocked. Nothing printed: see Failure modes before going on. The full checks are under Verify.Optional: install a login warning for boots that did not unlock. An image update that brings a new kernel leaves the old module in
/etc. The kernel's version check is expected to makeinsmodrefuse it (not yet seen on this box);load.shignores the failure,nvidialoads as usual and the card stays locked with no visible sign. This script, started at login, sends a desktop notification when the option is set but this boot did not unlock: module missing, module built for another kernel, or nogpu_unlock: unlockedline. On a headless box, check the journal after each update instead.mkdir -p bazzite cat > bazzite/check.sh <<'EOF' #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only # Run at login (XDG autostart): warn on the desktop when the unlock is opted in but did not happen, # most often because an image update brought a kernel the module was not built for. # GU_CMDLINE, GU_KVER and GU_NOTIFY override the inputs for testing. cmdline=$(cat "${GU_CMDLINE:-/proc/cmdline}") case " $cmdline " in *" gpu_unlock.targets="*) ;; *) exit 0 ;; esac ko=/etc/gpu-unlock/gpu_unlock.ko kver=${GU_KVER:-$(uname -r)} notify=${GU_NOTIFY:-notify-send} built=$(modinfo -F vermagic "$ko" 2>/dev/null | cut -d' ' -f1) if [ -z "$built" ]; then msg="$ko is missing: the unlock is not installed." elif [ "$built" != "$kver" ]; then msg="Module built for $built, running $kver: the card is locked. Rebuild the module for this kernel, reinstall it, then power off fully." elif ! journalctl -k -b 2>/dev/null | grep -q 'gpu_unlock: unlocked'; then msg="No 'gpu_unlock: unlocked' this boot. Check: journalctl -k -b | grep gpu_unlock" else exit 0 fi "$notify" -a gpu-unlock -u critical "gpu-unlock: not unlocked" "$msg" EOFcat > bazzite/gpu-unlock-check.desktop <<'EOF' [Desktop Entry] Type=Application Name=gpu-unlock check Comment=Warn when the GPU unlock did not happen this boot Exec=/bin/sh /etc/gpu-unlock/check.sh NoDisplay=true X-KDE-autostart-phase=2 EOF$ sha256sum bazzite/check.sh bazzite/gpu-unlock-check.desktop 98e136a627ad306b10ea98359351043d37dce0e7547547d3d3d8ade4429b8ef5 bazzite/check.sh aac408322d320c6679521e3d15490c903e1ad4ff1c78e73c49c29d342c2bf580 bazzite/gpu-unlock-check.desktopTest it before installing. On an unlocked boot it is silent; with
GU_KVERset to another kernel version andGU_NOTIFY=echo, it prints the notification it would send:$ sh bazzite/check.sh; echo "rc=$?" rc=0$ GU_KVER=0.0.0-test GU_NOTIFY=echo sh bazzite/check.sh -a gpu-unlock -u critical gpu-unlock: not unlocked Module built for 7.2.9-ogc3.1.fc44.x86_64, running 0.0.0-test: the card is locked. Rebuild the module for this kernel, reinstall it, then power off fully.Install it:
sudo install -Dm755 bazzite/check.sh /etc/gpu-unlock/check.sh install -Dm644 bazzite/gpu-unlock-check.desktop ~/.config/autostart/gpu-unlock-check.desktopcheck.shis not needed in the initramfs, so it is not tracked in step 6.Optional: make every boot entry unlocked. Only after Verify passed:
sudo rpm-ostree cleanup -rThis deletes the rollback deployment, so the boot menu no longer offers a locked entry or the state before step 6. Recovery then goes through the boot menu's edit screen.
Verify#
The kernel log of this boot has the unlock line (cut: the first of two command-line lines, and the ROM and FWSEC parse lines):
$ journalctl -k -b -o short-monotonic | grep -E 'gpu_unlock|NVRM: loading' … [ 2.654241] host kernel: Kernel command line: BOOT_IMAGE=(hd0,gpt2)/ostree/default-1b68a7f6d69d47119f25bf65e33a39241f28a815750d5d3417b702029a82d98d/vmlinuz-7.2.9-ogc3.1.fc44.x86_64 rhgb quiet root=UUID=<ROOT_UUID> rootflags=subvol=root rw ostree=/ostree/boot.0/default/1b68a7f6d69d47119f25bf65e33a39241f28a815750d5d3417b702029a82d98d/0 bluetooth.disable_ertm=1 gpu_unlock.targets=all … [ 7.012680] host kernel: pci 0000:10:00.0: gpu_unlock: unlocked [ 8.947048] host kernel: NVRM: loading NVIDIA UNIX Open Kernel Module for x86_64 615.78.08 Release Build (akmods@) Thu Oct 15 08:44:23 UTC 2026Expected:
gpu_unlock: unlockedon your card's PCI address, about two seconds beforeNVRM: loading. Nogpu_unlocklines at all means the overlay or the rule did not reach the initramfs; see Failure modes.The overlay is tracked and the boot entry carries it and the option:
$ rpm-ostree initramfs-etc Tracked files: /etc/gpu-unlock/firmware/gpu-unlock/610.43.03/booter_load_ga102.bin /etc/gpu-unlock/firmware/gpu-unlock/610.43.03/gsp_boot_desc_ga102.bin /etc/gpu-unlock/firmware/gpu-unlock/610.43.03/gsp_boot_ga102.bin /etc/gpu-unlock/firmware/gpu-unlock/610.43.03/gsp_ga10x.bin /etc/gpu-unlock/firmware/gpu-unlock/610.43.03/v67-ga102-610.43.03.bin /etc/gpu-unlock/gpu_unlock.ko /etc/gpu-unlock/load.sh /etc/modprobe.d/gpu-unlock.conf$ grep -E '^(options|initrd)' /boot/loader/entries/*.conf options rhgb quiet root=UUID=<ROOT_UUID> rootflags=subvol=root rw ostree=/ostree/boot.0/default/1b68a7f6d69d47119f25bf65e33a39241f28a815750d5d3417b702029a82d98d/0 bluetooth.disable_ertm=1 gpu_unlock.targets=all initrd /ostree/default-1b68a7f6d69d47119f25bf65e33a39241f28a815750d5d3417b702029a82d98d/initramfs-7.2.9-ogc3.1.fc44.x86_64.img initrd /ostree/initramfs-overlays/478bc1e8a230630f6402e2852948414ca0f13a0851599482029df49fb4a1c741.imgExpected: the eight tracked paths; a second
initrdline from/ostree/initramfs-overlays/;gpu_unlock.targets=allinoptions.nvidiadrives the card as usual:$ nvidia-smi --query-gpu=name,pci.bus_id,driver_version,vbios_version --format=csv name, pci.bus_id, driver_version, vbios_version NVIDIA GeForce RTX 3090, 00000000:10:00.0, 615.78.08, 94.02.26.80.8C
The speed-up itself shows only in FP16/BF16 math with FP32 accumulation and in TF32. On this card a cuBLAS 8192³ BF16 GEMM at a 280 W power limit went from 74.9 to 128 TFLOPS; the measurements are in the RTX 3090 unlock post.
Redo after a kernel update#
[UNVERIFIED: not exercised; no image update with a new kernel has arrived on the tested box since the install]
After an image update, compare the module's kernel with the running one:
$ modinfo -F vermagic /etc/gpu-unlock/gpu_unlock.ko; uname -r
7.2.9-ogc3.1.fc44.x86_64 SMP preempt mod_unload
7.2.9-ogc3.1.fc44.x86_64
If the first word differs from the second line, you are booted into the new kernel: repeat step 2 in ~/gpu-unlock (git -C ~/gpu-unlock status must show no changes). The firmware is pinned and already in /etc. Then:
sudo install -Dm644 kernel/gpu_unlock/gpu_unlock.ko /etc/gpu-unlock/gpu_unlock.ko
then step 6 (--force-sync builds a new deployment with the current tracked files) and step 8. The option is already set; step 7 is not needed.
Recovery: black screen after an unlock attempt#
- Hold the power button until the machine is off, or pull the plug. Upstream says to power off before trying again; do not count on a reboot.
- Power on and open the GRUB menu. Bazzite's docs: if the menu does not show, press Esc once and tap Down; if the menu timeout is set to "Skip Menu", hold Shift or F8. Select the entry, press
e, deletegpu_unlock.targets=allfrom the line that holds the kernel options, and boot with Ctrl+X. If step 11 was not run, theostree:1entry is the deployment from before step 6 and boots without the option. - Once logged in, remove the option for good:
sudo rpm-ostree kargs --delete-if-present=gpu_unlock.targets=all, then power off fully.
Undo / rollback#
Each line stands alone; stop when the box is where you want it.
| What | How |
|---|---|
| Skip the unlock for one boot | boot menu → e → delete gpu_unlock.targets=all → Ctrl+X |
| Go back to the deployment before step 6 | boot menu → the previous entry, or sudo rpm-ostree rollback (only before step 11; also drops anything else that came with the newer deployment, an image update included) |
| Stop unlocking, keep the files | sudo rpm-ostree kargs --delete-if-present=gpu_unlock.targets=all, then power off fully |
| Remove the files and the overlay | the block below [UNVERIFIED: rollback not exercised] |
F=/etc/gpu-unlock/firmware/gpu-unlock/610.43.03
sudo rpm-ostree initramfs-etc \
--untrack=/etc/gpu-unlock/gpu_unlock.ko --untrack=/etc/gpu-unlock/load.sh \
--untrack=/etc/modprobe.d/gpu-unlock.conf \
--untrack=$F/booter_load_ga102.bin --untrack=$F/gsp_ga10x.bin --untrack=$F/gsp_boot_ga102.bin \
--untrack=$F/gsp_boot_desc_ga102.bin --untrack=$F/v67-ga102-610.43.03.bin
sudo rm -f /etc/modprobe.d/gpu-unlock.conf
sudo rm -rf /etc/gpu-unlock
rm -f ~/.config/autostart/gpu-unlock-check.desktop
sudo rpm-ostree kargs --delete-if-present=gpu_unlock.targets=all
systemctl poweroff
Expected after the power off: rpm-ostree initramfs-etc lists no gpu-unlock path, ls /etc/gpu-unlock /etc/modprobe.d/gpu-unlock.conf fails, cat /proc/cmdline has no gpu_unlock. Once the option is gone the module does nothing, even if it still loads; after a full power off the card runs at stock rate again. The clone ~/gpu-unlock and the download cache ~/.cache/gpu-unlock (about 340 MB of inputs plus the generated files) are left in place; delete them by hand if you want the space back.
Failure modes#
| Symptom (exact message) | Cause | Fix |
|---|---|---|
poll timeout: reg=0x110100 last=0x10 mask=0x10 expected=0x0 budget_us=1000, then failure unlock_610.c:735 unlock_transaction_fwsec error=-110 (issue #20; the first line also in #80) | Module built from fc83321 on an RTX 3090 | Power off fully, rebuild from af28cbb (steps 1–2), reinstall the module (step 5), step 6, power off. |
gpu_unlock: unlock failed (-110) on each card, then nvidia cannot use the cards (issue #23) | Same fc83321 regression | As above; recover the screen first (Recovery). |
gpu_unlock: unlock failed (-16) (PR #79, layered-RPM path) | nvidia bound the card before gpu_unlock ran: the dracut drop-in gpu-unlock.conf sorts after Bazzite's 99-nvidia.conf. -16 is -EBUSY, which the module also returns for other reasons, so read the lines before it. | Not hit with these steps: the install nvidia rule runs the loader before any nvidia load. On the RPM path, name the drop-in 00-gpu-unlock.conf. |
initramfs regeneration and /etc overlay not compatible; use dracut arg -I instead (PR #79; rpm-ostree source) | rpm-ostree initramfs --enable while files are tracked with initramfs-etc, i.e. mixing this manual with the RPM path | Run the file-removal block in Undo (it untracks the files) before switching paths. |
sudo lsinitrd <image> | grep -E '…insmod$' prints nothing although insmod is in the image (my run) | lsinitrd lists symlinks as usr/bin/insmod -> kmod; a pattern anchored at $ misses them | Drop the $ anchor. |
Field notes#
- 2026-10-26 (evening), same box: Bazzite 44.20261023 bazzite-nvidia-open, kernel 7.2.9-ogc3.1.fc44, NVIDIA 615.78.08, RTX 3090 VBIOS 94.02.26.80.8C, gpu-unlock af28cbb, firmware pin 610.43.03: I re-ran the unprivileged half of this manual from a fresh clone in a scratch home directory, in the order above: Before you start, steps 1–4, step 9, the tests in step 10, Verify and the vermagic check. Clone, module build and
make assetstook under a minute together. The clone block above comes from a second clone a few minutes later, with a shortergit logformat. The five generated firmware files were byte-identical to the ones installed that morning; the freshly builtgpu_unlock.kowas not (cmpdiffers from byte 41), which is why step 5 compares your own build with your own copy. The pasted/etc, boot entry and journal output is from the boot running at the time, which had unlocked. On this box the RTC keeps local time, so early kernel lines in plainjournalctl -kcarry timestamps two hours ahead; the blocks above use-o catand-o short-monotonicfor that reason. - 2026-10-26, Bazzite 44.20261023 bazzite-nvidia-open, kernel 7.2.9-ogc3.1.fc44, NVIDIA 615.78.08, RTX 3090 VBIOS 94.02.26.80.8C, gpu-unlock af28cbb, firmware pin 610.43.03, Secure Boot off:
- I built from
af28cbbon purpose after reading the 3090 regression reports (#20, #23). - I installed around 13:00 while still booted into the previous deployment, with the 44.20261023 update already staged. Both
rpm-ostree initramfs-etcandrpm-ostree kargsbuilt on top of the staged tree; the kargs output listed the whole update's package diff. Both deployments carried the same kernel build, so the module matched. With a different kernel in the staged update it would be expected not to load. - First boot of the new deployment:
gpu_unlock: unlockedat 6.9 s on the journal's monotonic clock (the kernel's first line is at 2.6 s),NVRM: loadingat 8.8 s. - While checking the initramfs I grepped
lsinitrdoutput with a pattern anchored at$forinsmodand got nothing, althoughinsmodis there:lsinitrdprints symlinks asusr/bin/insmod -> kmod. Without the anchor it shows up. - Seen once: the first
systemctl powerofffrom the unlocked boot hung at the very end (power LED blinking) and needed the power button. The one later power-off, from a locked boot, and the later reboots, one of them from an unlocked boot, finished normally. I have not powered off from an unlocked boot since and did not investigate. - For a locked comparison I removed the option with
rpm-ostree kargs --delete=gpu_unlock.targets=all(new deployment, overlay kept), powered off (the hang above) and booted: nogpu_unlocklines, and a BF16 GEMM ran at 74.9 TFLOPS against 128 unlocked (280 W). I put the option back with--append-if-missing, gotgpu_unlock: unlockedagain, then ranrpm-ostree cleanup -r; one deployment is left and its entry has the option. - Later I skipped the unlock for one boot from the boot menu (
e, deleted the option). The plain reboot after that locked boot, in whichnvidiahad used the card, unlocked again: on this board a warm reboot cleared the state the module checks for, that one time. I still power off before a first unlock. - The login check (my installed copy differs from step 10 only in two message texts) stayed silent at login on the unlocked boot.
- I built from
Notes#
- Upstream
make install(DKMS and distro packages) does not work on rpm-ostree images; these steps replace it. The upstream README covers the module's behaviour: thetargetssyntax (all, or PCI addresses aslspci -Dprints them), suspend and resume, and the display card. gpu_unlock.targets=<PCI address>instead ofalllimits the unlock to one card; not tested here (one card).- After suspend to RAM the module unlocks the card again before
nvidiaresumes it (README).load.shleavesfirmware_class.pathpointing at/etc/gpu-unlock/firmwarefor that reason. Not tested on this box. - The other Bazzite route, upstream PR #131 (
packaging/atomic/install.sh, open and unmerged on 2026-10-26) and its manual twin PR #79: a layered local RPM,rpm-ostree initramfs --enable(the initramfs is then rebuilt locally on every update),depmodin%post(rpm-ostree runsdepmodonly when the kernel changes, so dracut skipped the module without it), and a drop-in named00-gpu-unlock.conf. It can sign the module for Secure Boot; #131 itself was tested with Secure Boot off, the signed path on hardware in #79 and in issue #80 (a ZOTAC 3090 on Bazzite 44.20261023, unlocked fromaf28cbb). The two routes do not mix (Failure modes, row 4). - Why the measured gain is uneven across local LLM engines: the RTX 3090 unlock post.
Filed under: nvidia, gpu, kernel, bazzite, rpm-ostree, local-llm
Sources#
- Zagitta/gpu-unlock — upstream; README at
af28cbbfortargets, resume and the display card;unlock_610.cataf28cbbfor the "already initialised" and "firmware did not stop" paths. - Issue #20 —
fc83321fails on RTX 3090s; open on 2026-10-26. Issue #23 —-110and cards unusable bynvidia; closed after the revert. Issue #80 — another 3090 on Bazzite, unlocked fromaf28cbb. - PR #79 and PR #131 — the layered-RPM route, with the
depmod,-16andinitramfs-etcconflict findings. - Compare
af28cbb...main— what changed upstream after the pin. - rpm-ostree(1) source —
initramfs-etc,kargs("Previous deployments are never changed"),cleanup -r,rollback. - modprobe.d(5) — the
installcommand,--ignore-install,$CMDLINE_OPTS. - Bazzite: rolling back system updates — how to open the GRUB menu,
ostree:1. - Kernel module signing — when unsigned modules load.