Skip to content

Commit e1d09dc

Browse files
kleinermalexdeucher
authored andcommitted
drm/amdgpu: Don't hang in amdgpu_flip_work_func on disabled crtc.
This fixes a regression introduced in Linux 4.4. This is a port of the same fix for radeon-kms in the patch "drm/radeon: Don't hang in radeon_flip_work_func on disabled crtc. (v2)" Limit the amount of time amdgpu_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 amdgpu_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. amdgpu_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 8e36f9d ("drm/amdgpu: 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> Reported-by: Bernd Steinhauser <linux@bernd-steinhauser.de> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> 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 2b8341b commit e1d09dc

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

drivers/gpu/drm/amd/amdgpu/amdgpu_display.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
7272

7373
struct drm_crtc *crtc = &amdgpuCrtc->base;
7474
unsigned long flags;
75-
unsigned i;
76-
int vpos, hpos, stat, min_udelay;
75+
unsigned i, repcnt = 4;
76+
int vpos, hpos, stat, min_udelay = 0;
7777
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
7878

7979
amdgpu_flip_wait_fence(adev, &work->excl);
@@ -96,7 +96,7 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
9696
* In practice this won't execute very often unless on very fast
9797
* machines because the time window for this to happen is very small.
9898
*/
99-
for (;;) {
99+
while (amdgpuCrtc->enabled && repcnt--) {
100100
/* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
101101
* start in hpos, and to the "fudged earlier" vblank start in
102102
* vpos.
@@ -114,10 +114,22 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
114114
/* Sleep at least until estimated real start of hw vblank */
115115
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
116116
min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
117+
if (min_udelay > vblank->framedur_ns / 2000) {
118+
/* Don't wait ridiculously long - something is wrong */
119+
repcnt = 0;
120+
break;
121+
}
117122
usleep_range(min_udelay, 2 * min_udelay);
118123
spin_lock_irqsave(&crtc->dev->event_lock, flags);
119124
};
120125

126+
if (!repcnt)
127+
DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, "
128+
"framedur %d, linedur %d, stat %d, vpos %d, "
129+
"hpos %d\n", work->crtc_id, min_udelay,
130+
vblank->framedur_ns / 1000,
131+
vblank->linedur_ns / 1000, stat, vpos, hpos);
132+
121133
/* do the flip (mmio) */
122134
adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base);
123135
/* set the flip status */

0 commit comments

Comments
 (0)