JFYI - going to try to review/test this ASAP, just been sidetracked chasing some big runtime PM issues in nouveau atm :S
On Fri, 2026-07-17 at 10:15 +0200, Arjan Filius wrote: > Hello Lyude, > > Yes, I did use generative AI to help refine and structure this (old) > patch. > It is completely based on work/debug/fixing I had done manually few > years back. > Generative AI helped me to send the message, but re-used my old > patched code, brushed up a bit. > And tested it this week on kernel 7.1.3, but previous tested on > 6.8.0.x and 6.9.9 kernels. > > In 2024 i submitted a bug report/fix/workaround (to prevent a full > lockup every day) on the Freedesktop GitLab under Issue 377: > https://gitlab.freedesktop.org/drm/nouveau/-/issues/377 > > Back then, my local workaround was a bit less elegant, and had a few > extra checks/debug statements. > I had almost forgotten about the issue since my workaround was > running fine. > When I recently booted up a clean mainline 7.1.3 kernel and saw the > system freeze again within a day and every day, > and had to fsck my disks due to it, I remembered the fix I once > debugged and posted in 2024. > I wanted to see if I could find a cleaner, more elegant solution and > used AI for it, to see if the patch could be in a nicer place, > but ended up to basically reproduce the fix i had written in 2024 > (without AI). > > > Regarding the two patches (A and B): > * **Patch B** was the version I compiled and tested locally with the > `nvkm_warn()` statement > so I could verify in `dmesg` that the standby transitions were > actually triggering the block and being safely bypassed. > * **Patch A** is the exact same check but silent (without the warning > logs). > > To avoid any confusion i repeat my patch, which does not produce any > logging every time, > but just avoids the lockup referencing null pointers when external > displays goes to sleep. > > > ```diff > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > index 51c045bdfcbb..ab1bc0898def 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > @@ -1157,6 +1157,10 @@ nv50_disp_super_2_2_dp(struct nvkm_head *head, > struct nvkm_ior *ior) > { > struct nvkm_subdev *subdev = &head->disp->engine.subdev; > > + /* If the DisplayPort link is inactive (0 lanes), skip > configuration */ > + if (ior->dp.nr == 0) > + return; > + > const u32 khz = head->asy.hz / 1000; > const u32 linkKBps = ior->dp.bw * 27000; > const u32 symbol = 100000; > ``` > > This completely fixes my lenovo T430(NVIDIA NVS 5400M) + 4338 Dock > and 2 external (multi-input) monitors freeze, proven over some years. > At the time the nvidia kernel driver on my T430 became an issue wih > newer kernels, and notice nouveau driver did pretty well too. > > Hope this claries it. > > Best regards, > Arjan Filius > > > > On 16 Jul 2026, at 22:40, [email protected] wrote: > > > > Hi, can you please clarify whether or not you used generative AI > > for > > this patch series? If so, this needs to be noted as part of the > > commit > > message. Additionally, both of these patches look to be identical > > except that one prints a message and the other doesn't. You can > > just > > send a single version, we will figure out how to handle that part > > after. > > > > On Thu, 2026-07-16 at 16:55 +0200, Arjan Filius wrote: > > > Dear Nouveau Maintainers, > > > > > > Please find below a proposed patch to prevent a division-by-zero > > > kernel oops and subsequent lockup in the Nouveau display > > > supervisor > > > engine when DisplayPort links transition to 0 active lanes. This > > > bug > > > was observed and patched/tested against the mainline kernel > > > version > > > **7.1.3**, but is present in all current upstream code. > > > > > > --- > > > > > > ### BRIEF EXPLANATION > > > > > > During screen sleep transitions or multi-input display switches > > > (KVM > > > switching), the active lane count of a DisplayPort link can > > > temporarily drop to 0 (`ior->dp.nr == 0`). > > > > > > When this occurs, the DisplayPort supervisor function > > > `nv50_disp_super_2_2_dp` is invoked via modeset interrupts. > > > Without > > > an early-exit guard, the function attempts to perform > > > symbols/hblank > > > and link rate calculations that divide by `ior->dp.nr`, resulting > > > in > > > a kernel division-by-zero math exception, lockup, and system > > > freeze. > > > > > > This patch adds an early-exit check at the beginning of > > > `nv50_disp_super_2_2_dp` to abort display configuration when the > > > link > > > training lanes count is zero. > > > > > > * **Tested Kernel Version:** `7.1.3` (mainline stable branch) > > > > > > --- > > > > > > ### EXTREMELY DETAILED TECHNICAL BREAKDOWN > > > > > > #### 1. Hardware Context > > > This bug has been consistently reproduced on a Lenovo ThinkPad > > > T430 > > > (Intel HD 4000 + discrete NVIDIA NVS 5400M / GF108 chipset) > > > connected > > > to a Lenovo ThinkPad Mini Dock Plus Series 3 (Model 4338) with > > > dual > > > external monitors on dock DisplayPorts (`DP-2` and `DP-3`). > > > > > > #### 2. Root Cause Analysis (Code Level) > > > Inside `drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c`, when > > > `ior- > > > > dp.nr == 0`, the driver executes the following lines in > > > `nv50_disp_super_2_2_dp()`: > > > > > > * **Line 1179:** Calculations for horizontal blank symbols: > > > ```c > > > h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr); > > > ``` > > > * **Line 1185:** Calculations for vertical blank symbols: > > > ```c > > > v = v - ((36 / ior->dp.nr) + 3) - 1; > > > ``` > > > * **Line 1190:** Calculations for active link data rate: > > > ```c > > > link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr; > > > ``` > > > > > > In all three instances, the denominator `ior->dp.nr` is `0`, > > > causing > > > an immediate kernel math exception in the display workqueue > > > (`nouveau_display_hpd_work` or `nv50_disp_super`), locking up the > > > graphics pipeline, freezing the screen, and hanging any reboot > > > attempts (requiring a physical hard reset). > > > > > > #### 3. Daily Workday Trigger Scenario > > > 1. The T430 (via dock) and a secondary workstation are both > > > connected to different inputs on the same external monitors. > > > 2. The monitors' input sources are switched to the secondary > > > workstation. > > > 3. At the end of the day, the secondary workstation goes to > > > sleep or > > > is shut down. > > > 4. With no active inputs, the external monitors enter power- > > > saving > > > standby mode. > > > 5. When the user subsequently wakes up or interacts with the > > > T430, > > > the driver attempts a modeset refresh. Since the monitors are > > > asleep > > > and inputs are switched away, the lanes count is read as `0`, > > > triggering the division-by-zero oops. > > > > > > #### 4. Stack Trace (Oops Log) > > > ```text > > > [di jul 23 10:28:25 2024] nouveau 0000:01:00.0: DRM: DDC > > > responded, > > > but no EDID for DP-2 > > > [di jul 23 10:28:25 2024] nouveau 0000:01:00.0: DRM: DDC > > > responded, > > > but no EDID for DP-3 > > > [di jul 23 10:28:26 2024] Workqueue: nvkm-disp nv50_disp_super > > > [nouveau] > > > [di jul 23 10:28:26 2024] RIP: > > > 0010:nv50_disp_super_2_2_dp+0xa7/0x320 > > > [nouveau] > > > [di jul 23 10:28:26 2024] ? nv50_disp_super_2_2_dp+0xa7/0x320 > > > [nouveau] > > > [di jul 23 10:28:26 2024] nv50_disp_super_2_2+0xf2/0x160 > > > [nouveau] > > > [di jul 23 10:28:26 2024] nv50_disp_super+0x139/0x280 [nouveau] > > > ``` > > > > > > #### 5. Verification Proof (Bypass Log) > > > With the patch applied and running on kernel `7.1.3-custom #2`, > > > the > > > screen standby transition was bypassed cleanly. The driver caught > > > the > > > lane count of 0, bypassed display configuration, and proceeded to > > > put > > > the graphics chip into its low-power runtime suspend state > > > (`suspended`) without locking up the OS: > > > ```text > > > [45162.232371] nouveau 0000:01:00.0: disp: DP link is inactive > > > (lanes > > > = 0), skipping display configuration. > > > [45162.232402] nouveau 0000:01:00.0: disp: DP link is inactive > > > (lanes > > > = 0), skipping display configuration. > > > [45178.605627] nouveau 0000:01:00.0: timeout > > > [45178.605629] WARNING: nv50_disp_dmac_fini+0x168/0x1c0 [nouveau] > > > [45180.608524] nouveau 0000:01:00.0: disp: ch 1 fini timeout, > > > 8e061008 > > > [45182.608521] nouveau 0000:01:00.0: timeout > > > [45182.608523] WARNING: nv50_disp_core_fini+0x143/0x190 [nouveau] > > > ``` > > > > > > --- > > > > > > Signed-off-by: Arjan Filius <[email protected]> > > > --- > > > > > > ### CHOOSE ONE OF THE PATCH OPTIONS BELOW TO APPEND: > > > > > > #### OPTION A: The Silent Production Patch (RECOMMENDED FOR > > > UPSTREAM) > > > *Note: Kernel maintainers generally reject warnings or print spam > > > for > > > expected hardware sleep/disconnect transitions. A silent return > > > is > > > the preferred upstream pattern.* > > > > > > ```diff > > > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > index 51c045bdfcbb..ab1bc0898def 100644 > > > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > @@ -1157,6 +1157,10 @@ nv50_disp_super_2_2_dp(struct nvkm_head > > > *head, > > > struct nvkm_ior *ior) > > > { > > > struct nvkm_subdev *subdev = &head->disp->engine.subdev; > > > > > > + /* If the DisplayPort link is inactive (0 lanes), skip > > > configuration */ > > > + if (ior->dp.nr == 0) > > > + return; > > > + > > > const u32 khz = head->asy.hz / 1000; > > > const u32 linkKBps = ior->dp.bw * 27000; > > > const u32 symbol = 100000; > > > ``` > > > > > > #### OPTION B: The Warning/Verbose Patch (What was tested > > > locally) > > > *Note: Prints warning logs to the kernel buffer for validation > > > and > > > troubleshooting.* > > > > > > ```diff > > > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > index 51c045bdfcbb..ab1bc0898def 100644 > > > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c > > > @@ -1157,6 +1157,12 @@ nv50_disp_super_2_2_dp(struct nvkm_head > > > *head, > > > struct nvkm_ior *ior) > > > { > > > struct nvkm_subdev *subdev = &head->disp->engine.subdev; > > > > > > + /* If the DisplayPort link is inactive (0 lanes), skip > > > configuration */ > > > + if (ior->dp.nr == 0) { > > > + nvkm_warn(subdev, "DP link is inactive (lanes = > > > 0), > > > skipping display configuration.\n"); > > > + return; > > > + } > > > + > > > const u32 khz = head->asy.hz / 1000; > > > const u32 linkKBps = ior->dp.bw * 27000; > > > const u32 symbol = 100000; > > > ``` > >
