Skip to content

Bound the GPFIFO-space wait in nvWriteGpEntry()#1192

Open
Lumzdas wants to merge 1 commit into
NVIDIA:mainfrom
Lumzdas:bound-gpfifo-wait
Open

Bound the GPFIFO-space wait in nvWriteGpEntry()#1192
Lumzdas wants to merge 1 commit into
NVIDIA:mainfrom
Lumzdas:bound-gpfifo-wait

Conversation

@Lumzdas

@Lumzdas Lumzdas commented Jun 11, 2026

Copy link
Copy Markdown

Bound the GPFIFO-space wait in nvWriteGpEntry()

Problem

nvWriteGpEntry() waits for a free GPFIFO entry in a loop with no timeout - it only exits when the GPU consumes an entry or a channel error is flagged. If a channel stalls without a flagged error, the loop spins forever in kernel context. When the spinning code path holds the nvkms lock, every subsequent modeset blocks in uninterruptible D state and the desktop deadlocks until reboot.

I hit this reproducibly via DIFR prefetch after resume from suspend; it appears to be the same failure as #1167 (same trace, also 595.71.05, suspend-triggered) and #1177 (same trace on 580.159.03, triggered during normal use).

Observed failure

Environment:

  • GPU: RTX 5090 (GB202), desktop; AMD iGPU also present, display driven by the 5090
  • Driver: 595.71.05 open kernel modules; NVreg_PreserveVideoMemoryAllocations enabled, nvidia-suspend/resume services active
  • Kernel: 7.0.3 (CachyOS, Arch-based), KDE Plasma / kwin_wayland
  • Suspend mode: deep (S3)

Resume completes cleanly at the kernel PM level (PM: suspend exit, nvidia-resume.service finished, no errors), but the screen stays frozen - the compositor's first DRM atomic commit after resume never returns:

INFO: task kwin_wayland:2815 blocked for more than 122 seconds.
task:kwin_wayland    state:D stack:0     pid:2815  tgid:2815  ppid:2809
Call Trace:
 __schedule+0x6ba/0x1e20
 schedule+0x55/0x180
 schedule_timeout+0x31/0x120
 __down_common+0xf4/0x1d0
 down+0x45/0x50
 nvkms_ioctl_from_kapi_try_pmlock+0x36/0x80 [nvidia_modeset]
 ApplyModeSetConfig+0xc7a/0xe00 [nvidia_modeset]
 nv_drm_atomic_check+0x220/0x270 [nvidia_drm]
 drm_atomic_check_only+0x18d/0x460
 drm_mode_atomic_ioctl+0x5ea/0x690
 drm_ioctl+0x306/0x4b0
 __x64_sys_ioctl+0x115/0x290

The hung task detector identifies the lock holder:

INFO: task kwin_wayland:2815 blocked on a semaphore likely last held by task nvidia-modeset/:274
task:nvidia-modeset/ state:R  running task     pid:274
 ? nvWriteGpEntry+0x10a/0x370 [nvidia_modeset]
 ? nvPushKickoff+0x24/0x40 [nvidia_modeset]
 ? PrefetchHelperSurfaceEvo+0x5aa/0x610 [nvidia_modeset]
 ? nvDIFRPrefetchSurfaces+0xed/0x240 [nvidia_modeset]
 ? DifrPrefetchEventDeferredWork+0x16/0x30 [nvidia_modeset]
 ? nvkms_kthread_q_callback+0x134/0x180 [nvidia_modeset]
 ? _main_loop+0xbc/0x140 [nvidia_modeset]

The nvidia-modeset/ kthread stays in R state burning exactly 100% of one core in kernel mode (stime in /proc/274/stat grows at 100 ticks/s). A sysrq-l NMI backtrace taken 29 minutes after resume still catches it at the same spot, confirming the loop never exits:

NMI backtrace for cpu 2
CPU: 2 UID: 0 PID: 274 Comm: nvidia-modeset/
RIP: 0010:nvWriteGpEntry+0x90/0x370 [nvidia_modeset]
Call Trace:
 nvPushKickoff+0x24/0x40 [nvidia_modeset]
 PrefetchHelperSurfaceEvo+0x5aa/0x610 [nvidia_modeset]
 nvDIFRPrefetchSurfaces+0xed/0x240 [nvidia_modeset]
 DifrPrefetchEventDeferredWork+0x16/0x30 [nvidia_modeset]
 nvkms_kthread_q_callback+0x134/0x180 [nvidia_modeset]
 _main_loop+0xbc/0x140 [nvidia_modeset]

So: the DIFR prefetch channel comes out of suspend with a full GPFIFO that the GPU never consumes, no channel error is flagged, and nvWriteGpEntry() spins forever inside DifrPrefetchEventDeferredWork with the nvkms lock held. Only recovery is a reboot - the compositor can't even be killed.

Fix

Bound the wait the same way IdleChannel() already does: poll nvPushImportGetMilliSeconds(), honor the channel's noTimeout flag, and on expiry of NV_PUSH_NOTIFIER_SHORT_TIMEOUT log an error and return FALSE.

  • Kickoff() already handles nvWriteGpEntry() returning FALSE (the channel-error path) and does not advance putOffset.
  • DIFR prefetch already anticipates a kickoff that never completes: the semaphore wait in PrefetchSingleSurface() times out and reports NV2080_CTRL_LPWR_DIFR_PREFETCH_FAIL_CE_HW_ERROR, after which (per the comment there) "DIFR will remain disabled until next driver load". That designed failure path was unreachable only because the kickoff one line above it could spin forever first.

With the timeout, the suspend-stall scenario degrades to: one 3-second wait, one logged error, DIFR disabled until next driver load, desktop keeps working.

Notes

  • __nvPushMakeRoom() has a similar unbounded wait. It is left untouched here: it returns void, is reached from many push sites, and once the first prefetch fails DIFR is disabled, so it is no longer reachable from this bug.
  • The DIFR prefetch channel is allocated with noTimeout = FALSE, so it opts into this timeout; channels created with noTimeout = TRUE keep the old behavior.
  • This patch (rebased onto 595.71.05, which has identical code in the affected file) is currently running on my machine; built against Linux 7.0.3 (clang/LLD).

@CLAassistant

CLAassistant commented Jun 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Lumzdas Lumzdas force-pushed the bound-gpfifo-wait branch from cf7592c to 2fc027e Compare June 11, 2026 11:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants