Skip to content

Commit 2b8341b

Browse files
kleinermalexdeucher
authored andcommitted
drm/radeon: Don't hang in radeon_flip_work_func on disabled crtc. (v2)
This fixes a regression introduced in Linux 4.4. Limit the amount of time radeon_flip_work_func can delay programming a page flip, by both limiting the maximum amount of time per wait cycle and the maximum number of wait cycles. Continue the flip if the limit is exceeded, even if that may result in a visual or timing glitch. This is to prevent a hang of page flips, as reported in fdo bug #93746: Disconnecting a DisplayPort display in parallel to a kms pageflip getting queued can cause the following hang of page flips and thereby an unusable desktop: 1. kms pageflip ioctl() queues pageflip -> queues execution of radeon_flip_work_func. 2. Hotunplug of display causes the driver to DPMS OFF the unplugged display. Display engine shuts down, scanout no longer moves, but stays at its resting position at start line of vblank. 3. radeon_flip_work_func executes while crtc is off, and due to the non-moving scanout position, the new flip delay code introduced into Linux 4.4 by commit 5b5561b ("drm/radeon: Fixup hw vblank counter/ts..") enters an infinite wait loop. 4. After reconnecting the display, the pageflip continues to hang in 3. and the display doesn't update its view of the desktop. This patch fixes the Linux 4.4 regression from fdo bug #93746 <https://bugs.freedesktop.org/show_bug.cgi?id=93746> v2: Skip wait immediately if !radeon_crtc->enabled, as suggested by Michel. Reported-by: Bernd Steinhauser <linux@bernd-steinhauser.de> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Tested-by: Bernd Steinhauser <linux@bernd-steinhauser.de> Cc: <stable@vger.kernel.org> # 4.4+ Cc: Michel Dänzer <michel.daenzer@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Reviewed-by: Michel Dänzer <michel.daenzer@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
1 parent 4fbbed4 commit 2b8341b

File tree

1 file changed

+15
-2
lines changed

1 file changed

+15
-2
lines changed

drivers/gpu/drm/radeon/radeon_display.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ static void radeon_flip_work_func(struct work_struct *__work)
403403
struct drm_crtc *crtc = &radeon_crtc->base;
404404
unsigned long flags;
405405
int r;
406-
int vpos, hpos, stat, min_udelay;
406+
int vpos, hpos, stat, min_udelay = 0;
407+
unsigned repcnt = 4;
407408
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
408409

409410
down_read(&rdev->exclusive_lock);
@@ -454,7 +455,7 @@ static void radeon_flip_work_func(struct work_struct *__work)
454455
* In practice this won't execute very often unless on very fast
455456
* machines because the time window for this to happen is very small.
456457
*/
457-
for (;;) {
458+
while (radeon_crtc->enabled && repcnt--) {
458459
/* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
459460
* start in hpos, and to the "fudged earlier" vblank start in
460461
* vpos.
@@ -472,10 +473,22 @@ static void radeon_flip_work_func(struct work_struct *__work)
472473
/* Sleep at least until estimated real start of hw vblank */
473474
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
474475
min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
476+
if (min_udelay > vblank->framedur_ns / 2000) {
477+
/* Don't wait ridiculously long - something is wrong */
478+
repcnt = 0;
479+
break;
480+
}
475481
usleep_range(min_udelay, 2 * min_udelay);
476482
spin_lock_irqsave(&crtc->dev->event_lock, flags);
477483
};
478484

485+
if (!repcnt)
486+
DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, "
487+
"framedur %d, linedur %d, stat %d, vpos %d, "
488+
"hpos %d\n", work->crtc_id, min_udelay,
489+
vblank->framedur_ns / 1000,
490+
vblank->linedur_ns / 1000, stat, vpos, hpos);
491+
479492
/* do the flip (mmio) */
480493
radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base);
481494

0 commit comments

Comments
 (0)