Since commit 52e6b1988334 ("drm/hypervdrm: Use vblank timer"), the driver's vblanks are generated by an hrtimer, and the default commit tail waits in drm_atomic_helper_wait_for_vblanks() for that timer to fire. In a VM, a timer can fire arbitrarily late, for instance when the host does not run the vCPU that the timer is queued on while the committing task keeps running on another vCPU. If the delay exceeds the helper's timeout, it warns, even though the display works fine: [CRTC:35:crtc-0] vblank wait timed out WARNING: drivers/gpu/drm/drm_atomic_helper.c:1921 at drm_atomic_helper_wait_for_vblanks.part.0+0x212/0x230, CPU#30: setfont/559 Call Trace: drm_atomic_helper_commit_tail commit_tail drm_atomic_helper_commit drm_atomic_commit drm_client_modeset_commit_atomic drm_client_modeset_commit_locked drm_fb_helper_pan_display fb_pan_display bit_update_start fbcon_switch redraw_screen fbcon_do_set_font fbcon_set_font con_font_op vt_ioctl This was seen intermittently on Azure VMs during boot, when setfont redraws the console right after hyperv_drm has taken it over. Cc: stable@vger.kernel.org Fixes: 52e6b1988334 ("drm/hypervdrm: Use vblank timer") Signed-off-by: Hamza Mahfooz --- This is inspired by VKMS (which also uses DRM's vblank timer), wait for the commit's flip_done instead. It completes once the vblank event that drm_crtc_vblank_atomic_flush() armed has been sent on the next timer tick. --- drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index cc5c5fbc8..8e5036051 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -504,6 +504,29 @@ static const struct drm_mode_config_funcs hv_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +/* + * Like drm_atomic_helper_commit_tail(), but wait for the commit's vblank + * events rather than for the vblank counter to advance. The host can delay + * the vblank timer for longer than drm_atomic_helper_wait_for_vblanks() + * waits before warning. + */ +static void hv_drm_atomic_commit_tail(struct drm_atomic_commit *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + drm_atomic_helper_commit_planes(dev, state, 0); + drm_atomic_helper_commit_modeset_enables(dev, state); + drm_atomic_helper_fake_vblank(state); + drm_atomic_helper_commit_hw_done(state); + drm_atomic_helper_wait_for_flip_done(dev, state); + drm_atomic_helper_cleanup_planes(dev, state); +} + +static const struct drm_mode_config_helper_funcs hv_drm_mode_config_helper_funcs = { + .atomic_commit_tail = hv_drm_atomic_commit_tail, +}; + int hv_drm_mode_config_init(struct hv_drm_device *hv) { struct drm_device *dev = &hv->dev; @@ -526,6 +549,7 @@ int hv_drm_mode_config_init(struct hv_drm_device *hv) dev->mode_config.prefer_shadow = 0; dev->mode_config.funcs = &hv_drm_mode_config_funcs; + dev->mode_config.helper_private = &hv_drm_mode_config_helper_funcs; ret = hv_drm_pipe_init(hv); if (ret) { -- 2.56.0