Re: [PATCH RFC v3] drm/client: Avoid warning on vblank timeout during modeset client waits
Krystian Kaniewski <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
Keep the timeout-warning suppression limited to the best-effort DRM
client path, but fix the public helper's error-source ambiguity. The
current wrapper emits the wait-timeout warning whenever the internal
helper returns `-ETIMEDOUT`. That return value can also come from
`drm_vblank_get()`, which propagates the driver's `enable_vblank()`
errno. If acquisition returns `-ETIMEDOUT`, no wait occurred, yet the
patch emits both the availability warning and a false wait-timeout warning.
Separate vblank acquisition from the quiet counter wait. Make the
private helper perform only the counter sampling and
`wait_event_timeout()` under a caller-held vblank reference. Keep the
client's existing outer `drm_crtc_vblank_get()` and
`drm_crtc_vblank_put()` pair, call the private helper between them, and
continue to ignore its timeout result.
In the exported `drm_crtc_wait_one_vblank()` wrapper, perform
`drm_vblank_get()` and its availability warning directly, and return an
acquisition error immediately. Only after successful acquisition should
it call the private helper and emit the timeout warning for
`-ETIMEDOUT`. Preserve the original warning-before-put ordering, return
values, one-second deadline, wait predicate, public signature, exported
ABI, and strict behavior of all direct public callers. Keep
`drm_atomic_helper_wait_for_vblanks()` unchanged.
Retain the cleaned commit-message style, corrected `Fixes` tag, AI
provenance, report links, and current recipient list. Update the
explanation to state that keeping acquisition in the public wrapper
prevents an `enable_vblank()` error from being mislabeled as a wait timeout.
On 8/6/2026 8:10 PM, syzbot wrote:
> On PREEMPT_RT kernels, a user-space task with elevated Real-Time (RT)
> priority can starve essential kernel threads. For example, the VKMS driver
> simulates vblank interrupts using hrtimers. On PREEMPT_RT, these timers run
> in the per-CPU timer threads at a low RT priority. If a user-space task
> elevates its priority above the timer thread and monopolizes the CPU, the
> timer thread is starved and the VKMS software vblank delivery is delayed
> beyond the timeout.
>
> This leads to a timeout when a worker thread waits for the vblank event.
> For instance, a console update triggers a framebuffer update, scheduling
> drm_fb_helper_damage_work() on the system workqueue. The worker thread
> eventually calls drm_client_modeset_wait_for_vblank() to synchronize the
> screen update with the vblank interval. Due to the starved timer, the wait
> times out and triggers a warning in drm_crtc_wait_one_vblank().
>
> Since this vblank wait in the client modeset path is only used for optional
> client update throttling, a timeout is acceptable and does not indicate a
> kernel bug. Therefore, a warning should not be triggered in this case.
>
> Introduce drm_crtc_wait_one_vblank_internal(), which performs the vblank
> wait without triggering a warning on timeout. This new function is used in
> drm_client_modeset_wait_for_vblank() to avoid the warning, while keeping
> the warning in drm_crtc_wait_one_vblank() for other callers where a timeout
> might still indicate an actual issue.
>
> Fixes: d8c4bddcd8bc ("drm/fb-helper: Synchronize dirty worker with vblank")
> Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
> Reported-by: [email protected]
> Closes: https://syzkaller.appspot.com/bug?extid=f59157955aba9d0cb43b
> Link: https://syzkaller.appspot.com/ai_job?id=527b7209-9098-44da-8634-7792d361dea5
> To: "David Airlie" <[email protected]>
> To: <[email protected]>
> To: "Maarten Lankhorst" <[email protected]>
> To: "Maxime Ripard" <[email protected]>
> To: "Simona Vetter" <[email protected]>
> To: "Thomas Zimmermann" <[email protected]>
> Cc: <[email protected]>
>
> ---
> v3:
> - Removed the raw kernel cut marker and full warning trace from the commit description.
> - Replaced first-person phrasing with impersonal wording in the commit description.
>
> v2:
> - Introduced drm_crtc_wait_one_vblank_internal() to allow waiting for vblank without warning on timeout.
> - Updated drm_client_modeset_wait_for_vblank() to use the new internal function, avoiding warnings during optional client update throttling.
> - Restored the warning in drm_crtc_wait_one_vblank() for other callers.
> https://lore.kernel.org/all/[email protected]/T/
>
> v1:
> https://lore.kernel.org/all/[email protected]/T/
> ---
> diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
> index 0080a8e95..7ff0f24a0 100644
> --- a/drivers/gpu/drm/drm_client_modeset.c
> +++ b/drivers/gpu/drm/drm_client_modeset.c
> @@ -1328,7 +1328,7 @@ int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned i
> */
> ret = drm_crtc_vblank_get(crtc);
> if (!ret) {
> - drm_crtc_wait_one_vblank(crtc);
> + drm_crtc_wait_one_vblank_internal(crtc);
> drm_crtc_vblank_put(crtc);
> }
>
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index f893b1e3a..6fd33672d 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -115,6 +115,7 @@ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
> int drm_vblank_get(struct drm_device *dev, unsigned int pipe);
> void drm_vblank_put(struct drm_device *dev, unsigned int pipe);
> u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe);
> +int drm_crtc_wait_one_vblank_internal(struct drm_crtc *crtc);
>
> /* drm_vblank_work.c */
> static inline void drm_vblank_flush_worker(struct drm_vblank_crtc *vblank)
> diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
> index f90fb2d13..7758e8265 100644
> --- a/drivers/gpu/drm/drm_vblank.c
> +++ b/drivers/gpu/drm/drm_vblank.c
> @@ -1297,17 +1297,7 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc)
> }
> EXPORT_SYMBOL(drm_crtc_vblank_put);
>
> -/**
> - * drm_crtc_wait_one_vblank - wait for one vblank
> - * @crtc: DRM crtc
> - *
> - * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> - * due to lack of driver support or because the crtc is off.
> - *
> - * Returns: 0 on success, negative error on failures.
> - */
> -int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> +int drm_crtc_wait_one_vblank_internal(struct drm_crtc *crtc)
> {
> struct drm_device *dev = crtc->dev;
> int pipe = drm_crtc_index(crtc);
> @@ -1326,12 +1316,30 @@ int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> last != drm_vblank_count(dev, pipe),
> msecs_to_jiffies(1000));
>
> - drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> -
> drm_vblank_put(dev, pipe);
>
> return ret ? 0 : -ETIMEDOUT;
> }
> +
> +/**
> + * drm_crtc_wait_one_vblank - wait for one vblank
> + * @crtc: DRM crtc
> + *
> + * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
> + * due to lack of driver support or because the crtc is off.
> + *
> + * Returns: 0 on success, negative error on failures.
> + */
> +int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> +{
> + int ret = drm_crtc_wait_one_vblank_internal(crtc);
> +
> + drm_WARN(crtc->dev, ret == -ETIMEDOUT, "vblank wait timed out on crtc %i\n",
> + drm_crtc_index(crtc));
> +
> + return ret;
> +}
> EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
>
> /**
>
>
> base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff