From: Ville Syrjälä ville.syrjala@linux.intel.com
[ Upstream commit 2de42f79bb21a412f40ade8831eb6fc445cb78a4 ]
Consider the following scenario: 1. nonblocking enable crtc 2. wait for the event 3. nonblocking disable crtc
On i915 this can lead to a spurious -EBUSY from step 3 on account of non-enabled planes getting the fake_commit in step 1 and we don't complete the fake_commit-> flip_done until drm_atomic_helper_commit_hw_done() which can happen a long time after the flip event was sent out.
This will become somewhat easy to hit on SKL+ once we start to add all the planes for the crtc to every modeset commit for the purposes of forcing a watermark register programming [1].
To make the race a little less pronounced let's complete fake_commit->flip_done after drm_atomic_helper_wait_for_flip_done(). For the single crtc case this should make the race quite theoretical, assuming drm_atomic_helper_wait_for_flip_done() actually has to wait for the real commit flip_done. In case the real commit flip_done gets completed singificantly before drm_atomic_helper_wait_for_flip_done(), or we are dealing with multiple crtcs whose vblanks don't line up nicely the race still exists.
[1] https://patchwork.freedesktop.org/patch/262670/
Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Fixes: 080de2e5be2d ("drm/atomic: Check for busy planes/connectors before setting the commit") Testcase: igt/kms_cursor_legacy/*nonblocking-modeset-vs-cursor-atomic Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com Link: https://patchwork.freedesktop.org/patch/msgid/20181122143412.11655-1-ville.s... Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/gpu/drm/drm_atomic_helper.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d8b526b7932c..b4e292a56046 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1445,6 +1445,9 @@ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", crtc->base.id, crtc->name); } + + if (old_state->fake_commit) + complete_all(&old_state->fake_commit->flip_done); } EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done);