fix: block NVIDIA HDA audio ALSA devices to prevent GPU wakeups - #107
Closed
felipecwb wants to merge 2 commits into
Closed
fix: block NVIDIA HDA audio ALSA devices to prevent GPU wakeups#107felipecwb wants to merge 2 commits into
felipecwb wants to merge 2 commits into
Conversation
Collaborator
|
looks good but it shouldn't be behind the |
felipecwb
marked this pull request as draft
July 27, 2026 13:01
felipecwb
force-pushed
the
fix/nvidia-hda-audio-wakeups
branch
3 times, most recently
from
July 27, 2026 19:33
37e35ed to
c2d3b53
Compare
NVIDIA GPUs are multi-function PCI devices: function 0 is the VGA controller and function 1 is the HDA audio controller. Both functions share the same PCI power domain, so when a sound server (pipewire or pulseaudio) opens an ALSA device belonging to the HDA function, the GPU is woken up from D3cold. The existing GPU block already covered the PCI sysfs directory of the audio function (via pci_to_inode's .0 -> .1 replacement), but sound servers do not access sysfs; they open the character devices under /dev/snd/ (e.g. controlC1, pcmC1D0p, hwC1D0). Without blocking those inodes the GPU keeps being woken up by periodical audio checks. Add nvidia_hda_snd_inodes() which scans /sys/class/sound for entries whose "device" symlink points at the GPU's HDA sibling PCI address (function 1), then collects the inode of the matching /dev/snd/* node. These inodes are blocked/unblocked alongside the existing nvidia and backlight inodes in block_gpu()/unblock_gpu(), so the fix applies in every mode (integrated, manual, smart) without requiring the experimental nvidia block flag.
felipecwb
force-pushed
the
fix/nvidia-hda-audio-wakeups
branch
from
July 27, 2026 19:42
c2d3b53 to
bb44c42
Compare
Author
|
didn't worked as expected |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The NVIDIA GPU block was not preventing GPU wake-ups caused by the NVIDIA HDA audio controller. When the GPU is in D3cold and a sound server (pipewire/pulseaudio) periodically probes the ALSA devices belonging to the HDA function, the GPU wakes up because both PCI functions share the same power domain.
Root cause
NVIDIA GPUs are multi-function PCI devices:
0000:64:00.0): VGA controller (class0x0300)0000:64:00.1): HDA audio controller (class0x0403)read_gpu()only collects devices with class0x03(display controllers), so the HDA audio function is never registered as a GPU device. The existingpci_to_inode()does try to block the.1PCI sysfs directory via the.0→.1string replacement, but sound servers don't touch sysfs — they open the character devices under/dev/snd/(e.g.controlC0,pcmC0D3p,hwC0D0). Those inodes were never added to the block list, so the eBPFfile_open/inode_permissionhooks let theopen()through, the HDA controller transitions to D0, and since it shares the power domain with the VGA function the whole GPU wakes up.NVIDIA PCI function numbering (hardware convention)
The function numbers are a hardware convention, not random:
This is confirmed by NVIDIA's own RTD3 power management documentation, which explicitly lists removing "Audio device PCI function, USB xHCI Host Controller function as well as USB Type-C UCSI Controller PCI function" — and by real-world
lspcioutput across many GPU generations:The PCI bus/device numbers (e.g.
64:00,01:00) vary per machine depending on motherboard PCIe topology, but the function numbers are fixed. Function 1 is always the HDA audio controller when the GPU is a multi-function device. Deriving the HDA sibling via.replace(".0", ".1")is the correct and robust approach — and is already what the existingpci_to_inode()does for the sysfs path. If the GPU is a single-function device with no audio function,0000:64:00.1simply doesn't exist and the function returns an empty list.ALSA sysfs layout (important for the matching logic)
In
/sys/class/sound, only thecardNentry has adevicesymlink that resolves directly to the PCI address. Sub-devices (controlC{N},pcmC{N}D{x}p,hwC{N}D{x}) have adevicesymlink that points back at thecardNsysfs dir, not the PCI device. Example from the bug reporter's machine:So the matching is done in two steps:
cardNwhosedevicesymlink resolves to the HDA PCI address (0000:64:00.1), and rememberN(the ALSA card number)./dev/snd/*node that belongs to that card (matchingC{N}followed byDor end-of-name, to avoid card 0 matching card 10+).For the bug reporter's system, this correctly blocks exactly:
controlC0,hwC0D0,pcmC0D3p,pcmC0D7p,pcmC0D8p,pcmC0D9p— and skips the AMD/Realtek cards 1 and 2.Fix
Added
nvidia_hda_snd_inodes()ininode.rs— takes the GPU's VGA PCI address, derives the HDA sibling as function 1, scans/sys/class/soundfor thecardNwhosedevicesymlink resolves to the HDA PCI address, then collects the inodes of all/dev/snd/*nodes belonging to that card. These inodes are blocked/unblocked alongside the existingnvidia_to_inodeandbacklight_to_inodecalls inblock_gpu()/unblock_gpu(), so the fix applies in every mode (integrated, manual, smart) without requiring the experimental nvidia block flag.This is not a flag-gated experiment — it's a straightforward fix to the per-GPU block path. The HDA inodes are now treated like the
/dev/nvidia0and backlight inodes: part of the standard NVIDIA block.Test Plan
cargo build/nix flake checkpassescardwire set integratedblocks the NVIDIA GPU;cardwire list --jsonshowsblocked: true/sys/bus/pci/devices/0000:64:00.0/power_state— GPU stays inD3coldeven when pipewire/pulseaudio runsdmesgno longer repeatsnvidia 0000:64:00.0: Enabling HDA controllercardwire set hybridunblocks the GPU and the HDA ALSA devices are accessible againChecklist