Re: [PATCH RFC v6] drm/client: Avoid warning on vblank timeout during modeset client waits
Krystian Kaniewski <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
#syz upstream
On 8/11/2026 3:04 AM, 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.
>
> Keeping the vblank reference acquisition (drm_vblank_get()) in the public
> wrapper drm_crtc_wait_one_vblank() rather than moving it to the internal
> helper prevents an enable_vblank() error from being mislabeled as a wait
> timeout.
>
> 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=f37d4830-e66f-4bce-be00-e7ac1d70c469
> 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]>
>
> ---
> v6:
> - Convert drm_crtc_wait_one_vblank_internal() comment block to kernel-doc format
> - Remove the stack trace from the commit description
>
> v5:
> - Added a kernel-doc comment block for drm_crtc_wait_one_vblank_internal().
> https://lore.kernel.org/all/[email protected]/T/
>
> v4:
> - Keep vblank reference acquisition in the public drm_crtc_wait_one_vblank() wrapper instead of moving it to drm_crtc_wait_one_vblank_internal().
> - Update the commit description to explain how this prevents enable_vblank() errors from being mislabeled as wait timeouts.
> https://lore.kernel.org/all/[email protected]/T/
>
> 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.
> https://lore.kernel.org/all/[email protected]/T/
>
> 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..9cac7013b 100644
> --- a/drivers/gpu/drm/drm_vblank.c
> +++ b/drivers/gpu/drm/drm_vblank.c
> @@ -1297,6 +1297,32 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc)
> }
> EXPORT_SYMBOL(drm_crtc_vblank_put);
>
> +/**
> + * drm_crtc_wait_one_vblank_internal - wait for one vblank
> + * @crtc: DRM crtc
> + *
> + * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
> + * Every caller must hold a vblank reference across the complete wait.
> + *
> + * Returns: 0 on success, negative error on failures.
> + */
> +int drm_crtc_wait_one_vblank_internal(struct drm_crtc *crtc)
> +{
> + struct drm_device *dev = crtc->dev;
> + int pipe = drm_crtc_index(crtc);
> + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
> + int ret;
> + u64 last;
> +
> + last = drm_vblank_count(dev, pipe);
> +
> + ret = wait_event_timeout(vblank->queue,
> + last != drm_vblank_count(dev, pipe),
> + msecs_to_jiffies(1000));
> +
> + return ret ? 0 : -ETIMEDOUT;
> +}
> +
> /**
> * drm_crtc_wait_one_vblank - wait for one vblank
> * @crtc: DRM crtc
> @@ -1311,26 +1337,20 @@ int drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
> {
> struct drm_device *dev = crtc->dev;
> int pipe = drm_crtc_index(crtc);
> - struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
> int ret;
> - u64 last;
>
> ret = drm_vblank_get(dev, pipe);
> if (drm_WARN(dev, ret, "vblank not available on crtc %i, ret=%i\n",
> pipe, ret))
> return ret;
>
> - last = drm_vblank_count(dev, pipe);
> -
> - ret = wait_event_timeout(vblank->queue,
> - last != drm_vblank_count(dev, pipe),
> - msecs_to_jiffies(1000));
> + ret = drm_crtc_wait_one_vblank_internal(crtc);
>
> - drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe);
> + drm_WARN(dev, ret == -ETIMEDOUT, "vblank wait timed out on crtc %i\n", pipe);
>
> drm_vblank_put(dev, pipe);
>
> - return ret ? 0 : -ETIMEDOUT;
> + return ret;
> }
> EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
>
>
>
> base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff