Message ID | 1455305432-28770-4-git-send-email-mario.kleiner.de@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 02/12/2016 08:30 PM, Mario Kleiner wrote: > Changes to drm_update_vblank_count() in Linux 4.4 broke the > behaviour of the pre/post modeset functions as the new update > code doesn't deal with hw vblank counter resets inbetween calls > to drm_vblank_pre_modeset an drm_vblank_post_modeset, as it > should. > > This causes mistreatment of such hw counter resets as counter > wraparound, and thereby large forward jumps of the software > vblank counter which in turn cause vblank event dispatching > and vblank waits to fail/hang --> userspace clients hang. > > This symptom was reported on radeon-kms to cause a infinite > hang of KDE Plasma 5 shell's login procedure, preventing users > from logging in. > > Fix this by detecting when drm_update_vblank_count() is called > inside a pre->post modeset interval. If so, clamp valid vblank > increments to the safe values 0 and 1, pretty much restoring > the update behavior of the old update code of Linux 4.3 and > earlier. Also reset the last recorded hw vblank count at call > to drm_vblank_post_modeset() to be safe against hw that after > modesetting, dpms on etc. only fires its first vblank irq after > drm_vblank_post_modeset() was already called. > > Reported-by: Vlastimil Babka <vbabka@suse.cz> > Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> > Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> > Tested-by: Vlastimil Babka <vbabka@suse.cz> FWIW I have applied patches 1-4 of this v2 to 4.4.2 and it works fine so far on my radeon. Patch 6/6 was for nouveau so I ignored it, and 5/6 didn't have stable tag (and from the description I couldn't decide if I should include it or not). Thanks, Vlastimil
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 92ad62f..055b0fa 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -222,6 +222,21 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, } /* + * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset + * interval? If so then vblank irqs keep running and it will likely + * happen that the hardware vblank counter is not trustworthy as it + * might reset at some point in that interval and vblank timestamps + * are not trustworthy either in that interval. Iow. this can result + * in a bogus diff >> 1 which must be avoided as it would cause + * random large forward jumps of the software vblank counter. + */ + if (diff > 1 && (vblank->inmodeset & 0x2)) { + DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u" + " due to pre-modeset.\n", pipe, diff); + diff = 1; + } + + /* * FIMXE: Need to replace this hack with proper seqlocks. * * Restrict the bump of the software vblank counter to a safe maximum @@ -1575,6 +1590,7 @@ void drm_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) if (vblank->inmodeset) { spin_lock_irqsave(&dev->vbl_lock, irqflags); dev->vblank_disable_allowed = true; + drm_reset_vblank_timestamp(dev, pipe); spin_unlock_irqrestore(&dev->vbl_lock, irqflags); if (vblank->inmodeset & 0x2)