All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes.
@ 2017-08-30 12:17 Maarten Lankhorst
  2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
                   ` (4 more replies)
  0 siblings, 5 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

This started out as a small set of 2 patches, but feedback made it 5. :(

Maarten Lankhorst (5):
  drm/i915: Always wait for flip_done
  drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done
  drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2.
  drm/atomic: Fix freeing connector/plane state too early by tracking
    commits, v2.
  drm/atomic: Make async plane update checks work as intended.

 drivers/gpu/drm/drm_atomic.c         |  11 +-
 drivers/gpu/drm/drm_atomic_helper.c  | 270 ++++++++++++++++++++++-------------
 drivers/gpu/drm/i915/i915_drv.h      |   3 +-
 drivers/gpu/drm/i915/intel_display.c |  93 +++---------
 include/drm/drm_atomic.h             |  13 +-
 include/drm/drm_connector.h          |   7 +
 include/drm/drm_crtc.h               |  23 ++-
 include/drm/drm_plane.h              |   8 ++
 8 files changed, 242 insertions(+), 186 deletions(-)

-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
@ 2017-08-30 12:17 ` Maarten Lankhorst
  2017-08-30 12:43   ` Daniel Vetter
  2017-09-04 11:18   ` [Intel-gfx] " Chris Wilson
  2017-08-30 12:17 ` [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done Maarten Lankhorst
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

The next commit removes the wait for flip_done in in
drm_atomic_helper_commit_cleanup_done, but we need it for the tests
to pass. Instead of using complicated vblank tracking which ends
up being ignored anyway, call the correct atomic helper. :)

RFC because I'm not completely sure what we want to do with the vblank waiting,
I think for now this patch is the right way to go until we decide because it
preserves the status quo when drm_crtc_commit was introduced.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |  3 +-
 drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
 2 files changed, 8 insertions(+), 78 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index cbbafbfb0a55..de19621864a9 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
 			    struct drm_atomic_state *old_state);
 	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
 			     struct drm_atomic_state *old_state);
-	void (*update_crtcs)(struct drm_atomic_state *state,
-			     unsigned int *crtc_vblank_mask);
+	void (*update_crtcs)(struct drm_atomic_state *state);
 	void (*audio_codec_enable)(struct drm_connector *connector,
 				   struct intel_encoder *encoder,
 				   const struct drm_display_mode *adjusted_mode);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 52c73b4dabaa..3f3cb96aa11e 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
 	return dev->driver->get_vblank_counter(dev, crtc->pipe);
 }
 
-static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
-					  struct drm_i915_private *dev_priv,
-					  unsigned crtc_mask)
-{
-	unsigned last_vblank_count[I915_MAX_PIPES];
-	enum pipe pipe;
-	int ret;
-
-	if (!crtc_mask)
-		return;
-
-	for_each_pipe(dev_priv, pipe) {
-		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
-								  pipe);
-
-		if (!((1 << pipe) & crtc_mask))
-			continue;
-
-		ret = drm_crtc_vblank_get(&crtc->base);
-		if (WARN_ON(ret != 0)) {
-			crtc_mask &= ~(1 << pipe);
-			continue;
-		}
-
-		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
-	}
-
-	for_each_pipe(dev_priv, pipe) {
-		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
-								  pipe);
-		long lret;
-
-		if (!((1 << pipe) & crtc_mask))
-			continue;
-
-		lret = wait_event_timeout(dev->vblank[pipe].queue,
-				last_vblank_count[pipe] !=
-					drm_crtc_vblank_count(&crtc->base),
-				msecs_to_jiffies(50));
-
-		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
-
-		drm_crtc_vblank_put(&crtc->base);
-	}
-}
-
-static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
-{
-	/* fb updated, need to unpin old fb */
-	if (crtc_state->fb_changed)
-		return true;
-
-	/* wm changes, need vblank before final wm's */
-	if (crtc_state->update_wm_post)
-		return true;
-
-	if (crtc_state->wm.need_postvbl_update)
-		return true;
-
-	return false;
-}
-
 static void intel_update_crtc(struct drm_crtc *crtc,
 			      struct drm_atomic_state *state,
 			      struct drm_crtc_state *old_crtc_state,
-			      struct drm_crtc_state *new_crtc_state,
-			      unsigned int *crtc_vblank_mask)
+			      struct drm_crtc_state *new_crtc_state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
@@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
 	}
 
 	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
-
-	if (needs_vblank_wait(pipe_config))
-		*crtc_vblank_mask |= drm_crtc_mask(crtc);
 }
 
-static void intel_update_crtcs(struct drm_atomic_state *state,
-			       unsigned int *crtc_vblank_mask)
+static void intel_update_crtcs(struct drm_atomic_state *state)
 {
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
@@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
 			continue;
 
 		intel_update_crtc(crtc, state, old_crtc_state,
-				  new_crtc_state, crtc_vblank_mask);
+				  new_crtc_state);
 	}
 }
 
-static void skl_update_crtcs(struct drm_atomic_state *state,
-			     unsigned int *crtc_vblank_mask)
+static void skl_update_crtcs(struct drm_atomic_state *state)
 {
 	struct drm_i915_private *dev_priv = to_i915(state->dev);
 	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
@@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
 				vbl_wait = true;
 
 			intel_update_crtc(crtc, state, old_crtc_state,
-					  new_crtc_state, crtc_vblank_mask);
+					  new_crtc_state);
 
 			if (vbl_wait)
 				intel_wait_for_vblank(dev_priv, pipe);
@@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 	struct intel_crtc_state *intel_cstate;
 	bool hw_check = intel_state->modeset;
 	u64 put_domains[I915_MAX_PIPES] = {};
-	unsigned crtc_vblank_mask = 0;
 	int i;
 
 	intel_atomic_commit_fence_wait(intel_state);
@@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
 
 	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
-	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
+	dev_priv->display.update_crtcs(state);
 
 	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
 	 * already, but still need the state for the delayed optimization. To
@@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 	 *   we don't need out special handling any more.
 	 */
 	if (!state->legacy_cursor_update)
-		intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
+		drm_atomic_helper_wait_for_flip_done(dev, state);
 
 	/*
 	 * Now that the vblank has passed, we can go ahead and program the
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done
  2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
  2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
@ 2017-08-30 12:17 ` Maarten Lankhorst
  2017-08-30 12:28   ` Daniel Vetter
  2017-08-30 12:17 ` [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2 Maarten Lankhorst
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

When commit synchronization through drm_crtc_commit was first
introduced, we tried to solve the problem of the flip_done
needing a reference count by blocking in cleanup_done.

This has been changed by commit 24835e442f28 ("drm: reference count
event->completion") which made the waits here no longer needed.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_atomic_helper.c | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 4e53aae9a1fb..11d0e94a2181 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1885,7 +1885,6 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
 	struct drm_crtc_state *new_crtc_state;
 	struct drm_crtc_commit *commit;
 	int i;
-	long ret;
 
 	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
 		commit = old_state->crtcs[i].commit;
@@ -1895,22 +1894,6 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
 		complete_all(&commit->cleanup_done);
 		WARN_ON(!try_wait_for_completion(&commit->hw_done));
 
-		/* commit_list borrows our reference, need to remove before we
-		 * clean up our drm_atomic_state. But only after it actually
-		 * completed, otherwise subsequent commits won't stall properly. */
-		if (try_wait_for_completion(&commit->flip_done))
-			goto del_commit;
-
-		/* We must wait for the vblank event to signal our completion
-		 * before releasing our reference, since the vblank work does
-		 * not hold a reference of its own. */
-		ret = wait_for_completion_timeout(&commit->flip_done,
-						  10*HZ);
-		if (ret == 0)
-			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
-				  crtc->base.id, crtc->name);
-
-del_commit:
 		spin_lock(&crtc->commit_lock);
 		list_del(&commit->commit_entry);
 		spin_unlock(&crtc->commit_lock);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2.
  2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
  2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
  2017-08-30 12:17 ` [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done Maarten Lankhorst
@ 2017-08-30 12:17 ` Maarten Lankhorst
  2017-08-30 13:09   ` Laurent Pinchart
  2017-08-30 12:17 ` [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2 Maarten Lankhorst
  2017-08-30 12:17 ` [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended Maarten Lankhorst
  4 siblings, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

Most code only cares about the current commit or previous commit.
Fortuantely we already have a place to track those. Move it to
drm_crtc_state where it belongs. :)

The per-crtc commit_list is kept for places where we have to look
deeper than the current or previous commit for checking whether to stall
on unpin. This is used in drm_atomic_helper_setup_commit and
intel_has_pending_fb_unpin.

Changes since v1:
- Update kerneldoc for drm_crtc.commit_list. (danvet)

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---
 drivers/gpu/drm/drm_atomic.c        |  7 ---
 drivers/gpu/drm/drm_atomic_helper.c | 92 ++++++++++---------------------------
 include/drm/drm_atomic.h            |  1 -
 include/drm/drm_crtc.h              | 23 ++++++++--
 4 files changed, 42 insertions(+), 81 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 2fd383d7253a..2cce48f203e0 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 		crtc->funcs->atomic_destroy_state(crtc,
 						  state->crtcs[i].state);
 
-		if (state->crtcs[i].commit) {
-			kfree(state->crtcs[i].commit->event);
-			state->crtcs[i].commit->event = NULL;
-			drm_crtc_commit_put(state->crtcs[i].commit);
-		}
-
-		state->crtcs[i].commit = NULL;
 		state->crtcs[i].ptr = NULL;
 		state->crtcs[i].state = NULL;
 	}
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 11d0e94a2181..8ccb8b6536c0 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
 void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
 					  struct drm_atomic_state *old_state)
 {
-	struct drm_crtc_state *unused;
+	struct drm_crtc_state *new_crtc_state;
 	struct drm_crtc *crtc;
 	int i;
 
-	for_each_new_crtc_in_state(old_state, crtc, unused, i) {
-		struct drm_crtc_commit *commit = old_state->crtcs[i].commit;
+	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
+		struct drm_crtc_commit *commit = new_crtc_state->commit;
 		int ret;
 
 		if (!commit)
@@ -1388,11 +1388,10 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
 {
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *crtc_state;
-	struct drm_crtc_commit *commit;
 	struct drm_plane *__plane, *plane = NULL;
 	struct drm_plane_state *__plane_state, *plane_state = NULL;
 	const struct drm_plane_helper_funcs *funcs;
-	int i, j, n_planes = 0;
+	int i, n_planes = 0;
 
 	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
 		if (drm_atomic_crtc_needs_modeset(crtc_state))
@@ -1420,33 +1419,10 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
 		return -EINVAL;
 
 	/*
-	 * Don't do an async update if there is an outstanding commit modifying
+	 * TODO: Don't do an async update if there is an outstanding commit modifying
 	 * the plane.  This prevents our async update's changes from getting
 	 * overridden by a previous synchronous update's state.
 	 */
-	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
-		if (plane->crtc != crtc)
-			continue;
-
-		spin_lock(&crtc->commit_lock);
-		commit = list_first_entry_or_null(&crtc->commit_list,
-						  struct drm_crtc_commit,
-						  commit_entry);
-		if (!commit) {
-			spin_unlock(&crtc->commit_lock);
-			continue;
-		}
-		spin_unlock(&crtc->commit_lock);
-
-		if (!crtc->state->state)
-			continue;
-
-		for_each_plane_in_state(crtc->state->state, __plane,
-					__plane_state, j) {
-			if (__plane == plane)
-				return -EINVAL;
-		}
-	}
 
 	return funcs->atomic_async_check(plane, plane_state);
 }
@@ -1731,7 +1707,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 		kref_init(&commit->ref);
 		commit->crtc = crtc;
 
-		state->crtcs[i].commit = commit;
+		new_crtc_state->commit = commit;
 
 		ret = stall_checks(crtc, nonblock);
 		if (ret)
@@ -1769,22 +1745,6 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
 
-
-static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
-{
-	struct drm_crtc_commit *commit;
-	int i = 0;
-
-	list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
-		/* skip the first entry, that's the current commit */
-		if (i == 1)
-			return commit;
-		i++;
-	}
-
-	return NULL;
-}
-
 /**
  * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
  * @old_state: atomic state object with old state structures
@@ -1800,17 +1760,13 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
 void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
 {
 	struct drm_crtc *crtc;
-	struct drm_crtc_state *new_crtc_state;
+	struct drm_crtc_state *old_crtc_state;
 	struct drm_crtc_commit *commit;
 	int i;
 	long ret;
 
-	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-		spin_lock(&crtc->commit_lock);
-		commit = preceeding_commit(crtc);
-		if (commit)
-			drm_crtc_commit_get(commit);
-		spin_unlock(&crtc->commit_lock);
+	for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+		commit = old_crtc_state->commit;
 
 		if (!commit)
 			continue;
@@ -1828,8 +1784,6 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
 		if (ret == 0)
 			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
 				  crtc->base.id, crtc->name);
-
-		drm_crtc_commit_put(commit);
 	}
 }
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
@@ -1857,7 +1811,7 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
 	int i;
 
 	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-		commit = old_state->crtcs[i].commit;
+		commit = new_crtc_state->commit;
 		if (!commit)
 			continue;
 
@@ -1887,7 +1841,7 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
 	int i;
 
 	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
-		commit = old_state->crtcs[i].commit;
+		commit = new_crtc_state->commit;
 		if (WARN_ON(!commit))
 			continue;
 
@@ -2277,20 +2231,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
 	struct drm_private_state *old_obj_state, *new_obj_state;
 
 	if (stall) {
-		for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
-			spin_lock(&crtc->commit_lock);
-			commit = list_first_entry_or_null(&crtc->commit_list,
-					struct drm_crtc_commit, commit_entry);
-			if (commit)
-				drm_crtc_commit_get(commit);
-			spin_unlock(&crtc->commit_lock);
+		for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
+			commit = old_crtc_state->commit;
 
 			if (!commit)
 				continue;
 
 			ret = wait_for_completion_interruptible(&commit->hw_done);
-			drm_crtc_commit_put(commit);
-
 			if (ret)
 				return ret;
 		}
@@ -2315,13 +2262,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
 		state->crtcs[i].state = old_crtc_state;
 		crtc->state = new_crtc_state;
 
-		if (state->crtcs[i].commit) {
+		if (new_crtc_state->commit) {
 			spin_lock(&crtc->commit_lock);
-			list_add(&state->crtcs[i].commit->commit_entry,
+			list_add(&new_crtc_state->commit->commit_entry,
 				 &crtc->commit_list);
 			spin_unlock(&crtc->commit_lock);
 
-			state->crtcs[i].commit->event = NULL;
+			new_crtc_state->commit->event = NULL;
 		}
 	}
 
@@ -3169,6 +3116,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
 	state->connectors_changed = false;
 	state->color_mgmt_changed = false;
 	state->zpos_changed = false;
+	state->commit = NULL;
 	state->event = NULL;
 	state->pageflip_flags = 0;
 }
@@ -3207,6 +3155,12 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
  */
 void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
 {
+	if (state->commit) {
+		kfree(state->commit->event);
+		state->commit->event = NULL;
+		drm_crtc_commit_put(state->commit);
+	}
+
 	drm_property_blob_put(state->mode_blob);
 	drm_property_blob_put(state->degamma_lut);
 	drm_property_blob_put(state->ctm);
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 8a5808eb5628..e76d9f95c191 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -144,7 +144,6 @@ struct __drm_planes_state {
 struct __drm_crtcs_state {
 	struct drm_crtc *ptr;
 	struct drm_crtc_state *state, *old_state, *new_state;
-	struct drm_crtc_commit *commit;
 	s32 __user *out_fence_ptr;
 	unsigned last_vblank_count;
 };
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 1a642020e306..1a01ff4ea023 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -253,6 +253,15 @@ struct drm_crtc_state {
 	 */
 	struct drm_pending_vblank_event *event;
 
+	/**
+	 * @commit:
+	 *
+	 * This tracks how the commit for this update proceeds through the
+	 * various phases. This is never cleared, except when we destroy the
+	 * state, so that subsequent commits can synchronize with previous ones.
+	 */
+	struct drm_crtc_commit *commit;
+
 	struct drm_atomic_state *state;
 };
 
@@ -808,10 +817,16 @@ struct drm_crtc {
 	 * @commit_list:
 	 *
 	 * List of &drm_crtc_commit structures tracking pending commits.
-	 * Protected by @commit_lock. This list doesn't hold its own full
-	 * reference, but burrows it from the ongoing commit. Commit entries
-	 * must be removed from this list once the commit is fully completed,
-	 * but before it's correspoding &drm_atomic_state gets destroyed.
+	 * Protected by @commit_lock. This list holds its own full reference,
+	 * as does the ongoing commit.
+	 *
+	 * "Note that the commit for a state change is also tracked in
+	 * &drm_crtc_state.commit. For accessing the immediately preceeding
+	 * commit in an atomic update it is recommended to just use that
+	 * pointer in the old CRTC state, since accessing that doesn't need
+	 * any locking or list-walking. @commit_list should only be used to
+	 * stall for framebuffer cleanup that's signalled through
+	 * &drm_crtc_commit.cleanup_done."
 	 */
 	struct list_head commit_list;
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
                   ` (2 preceding siblings ...)
  2017-08-30 12:17 ` [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2 Maarten Lankhorst
@ 2017-08-30 12:17 ` Maarten Lankhorst
  2017-08-30 12:40   ` Daniel Vetter
  2017-08-30 14:10   ` Laurent Pinchart
  2017-08-30 12:17 ` [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended Maarten Lankhorst
  4 siblings, 2 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx, Laurent Pinchart

Currently we neatly track the crtc state, but forget to look at
plane/connector state.

When doing a nonblocking modeset, immediately followed by a setprop
before the modeset completes, the setprop will see the modesets new
state as the old state and free it.

This has to be solved by waiting for hw_done on the connector, even
if it's not assigned to a crtc. When a connector is unbound we take
the last crtc commit, and when it stays unbound we create a new
fake crtc commit for that gets signaled on hw_done for all the
planes/connectors.

We wait for it the same way as we do for crtc's, which will make
sure we never run into a use-after-free situation.

Changes since v1:
- Only create a single disable commit. (danvet)
- Fix leak in intel_legacy_cursor_update.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/drm_atomic.c         |   4 +
 drivers/gpu/drm/drm_atomic_helper.c  | 156 +++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/i915/intel_display.c |   2 +
 include/drm/drm_atomic.h             |  12 +++
 include/drm/drm_connector.h          |   7 ++
 include/drm/drm_plane.h              |   7 ++
 6 files changed, 182 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 2cce48f203e0..75f5f74de9bf 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 	}
 	state->num_private_objs = 0;
 
+	if (state->fake_commit) {
+		drm_crtc_commit_put(state->fake_commit);
+		state->fake_commit = NULL;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
 
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 8ccb8b6536c0..034f563fb130 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion *completion)
 	drm_crtc_commit_put(commit);
 }
 
+static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
+{
+	init_completion(&commit->flip_done);
+	init_completion(&commit->hw_done);
+	init_completion(&commit->cleanup_done);
+	INIT_LIST_HEAD(&commit->commit_entry);
+	kref_init(&commit->ref);
+	commit->crtc = crtc;
+}
+
+static struct drm_crtc_commit *
+fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
+{
+	struct drm_crtc_commit *commit;
+
+	if (crtc) {
+		struct drm_crtc_state *new_crtc_state;
+
+		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+		commit = new_crtc_state->commit;
+	} else if (!state->fake_commit) {
+		state->fake_commit = commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+		if (!commit)
+			return NULL;
+
+		init_commit(commit, NULL);
+	} else
+		commit = state->fake_commit;
+
+	drm_crtc_commit_get(commit);
+	return commit;
+}
+
 /**
  * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
  * @state: new modeset state to be committed
@@ -1692,6 +1726,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 {
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+	struct drm_connector *conn;
+	struct drm_connector_state *old_conn_state, *new_conn_state;
+	struct drm_plane *plane;
+	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct drm_crtc_commit *commit;
 	int i, ret;
 
@@ -1700,12 +1738,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 		if (!commit)
 			return -ENOMEM;
 
-		init_completion(&commit->flip_done);
-		init_completion(&commit->hw_done);
-		init_completion(&commit->cleanup_done);
-		INIT_LIST_HEAD(&commit->commit_entry);
-		kref_init(&commit->ref);
-		commit->crtc = crtc;
+		init_commit(commit, crtc);
 
 		new_crtc_state->commit = commit;
 
@@ -1741,6 +1774,36 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 		drm_crtc_commit_get(commit);
 	}
 
+	for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
+		if (new_conn_state->crtc)
+			continue;
+
+		if (nonblock && old_conn_state->commit &&
+		    !try_wait_for_completion(&old_conn_state->commit->flip_done))
+			return -EBUSY;
+
+		commit = fake_or_crtc_commit(state, old_conn_state->crtc);
+		if (!commit)
+			return -ENOMEM;
+
+		new_conn_state->commit = commit;
+	}
+
+	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
+		if (new_plane_state->crtc)
+			continue;
+
+		if (nonblock && old_plane_state->commit &&
+		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
+			return -EBUSY;
+
+		commit = fake_or_crtc_commit(state, old_plane_state->crtc);
+		if (!commit)
+			return -ENOMEM;
+
+		new_plane_state->commit = commit;
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
@@ -1761,6 +1824,10 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
 {
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
+	struct drm_plane *plane;
+	struct drm_plane_state *old_plane_state;
+	struct drm_connector *conn;
+	struct drm_connector_state *old_conn_state;
 	struct drm_crtc_commit *commit;
 	int i;
 	long ret;
@@ -1785,6 +1852,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
 			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
 				  crtc->base.id, crtc->name);
 	}
+
+	for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
+		commit = old_conn_state->commit;
+
+		if (!commit)
+			continue;
+
+		ret = wait_for_completion_timeout(&commit->hw_done,
+						  10*HZ);
+		if (ret == 0)
+			DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
+				  conn->base.id, conn->name);
+
+		/* Currently no support for overwriting flips, hence
+		 * stall for previous one to execute completely. */
+		ret = wait_for_completion_timeout(&commit->flip_done,
+						  10*HZ);
+		if (ret == 0)
+			DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
+				  conn->base.id, conn->name);
+	}
+
+	for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
+		commit = old_plane_state->commit;
+
+		if (!commit)
+			continue;
+
+		ret = wait_for_completion_timeout(&commit->hw_done,
+						  10*HZ);
+		if (ret == 0)
+			DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
+				  plane->base.id, plane->name);
+
+		/* Currently no support for overwriting flips, hence
+		 * stall for previous one to execute completely. */
+		ret = wait_for_completion_timeout(&commit->flip_done,
+						  10*HZ);
+		if (ret == 0)
+			DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
+				  plane->base.id, plane->name);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
 
@@ -1819,6 +1928,11 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
 		WARN_ON(new_crtc_state->event);
 		complete_all(&commit->hw_done);
 	}
+
+	if (old_state->fake_commit) {
+		complete_all(&old_state->fake_commit->hw_done);
+		complete_all(&old_state->fake_commit->flip_done);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
 
@@ -2241,6 +2355,28 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
 			if (ret)
 				return ret;
 		}
+
+		for_each_old_connector_in_state(state, connector, old_conn_state, i) {
+			commit = old_conn_state->commit;
+
+			if (!commit)
+				continue;
+
+			ret = wait_for_completion_interruptible(&commit->hw_done);
+			if (ret)
+				return ret;
+		}
+
+		for_each_old_plane_in_state(state, plane, old_plane_state, i) {
+			commit = old_plane_state->commit;
+
+			if (!commit)
+				continue;
+
+			ret = wait_for_completion_interruptible(&commit->hw_done);
+			if (ret)
+				return ret;
+		}
 	}
 
 	for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) {
@@ -3223,6 +3359,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
 		drm_framebuffer_get(state->fb);
 
 	state->fence = NULL;
+	state->commit = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
 
@@ -3264,6 +3401,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
 
 	if (state->fence)
 		dma_fence_put(state->fence);
+
+	if (state->commit)
+		drm_crtc_commit_put(state->commit);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
 
@@ -3342,6 +3482,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
 	memcpy(state, connector->state, sizeof(*state));
 	if (state->crtc)
 		drm_connector_get(connector);
+	state->commit = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
 
@@ -3468,6 +3609,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
 {
 	if (state->crtc)
 		drm_connector_put(state->connector);
+
+	if (state->commit)
+		drm_crtc_commit_put(state->commit);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3f3cb96aa11e..70ce02753177 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13109,8 +13109,10 @@ intel_legacy_cursor_update(struct drm_plane *plane,
 
 	/* Swap plane state */
 	new_plane_state->fence = old_plane_state->fence;
+	new_plane_state->commit = old_plane_state->commit;
 	*to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state);
 	new_plane_state->fence = NULL;
+	new_plane_state->commit = NULL;
 	new_plane_state->fb = old_fb;
 	to_intel_plane_state(new_plane_state)->vma = NULL;
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index e76d9f95c191..ba976f4a4938 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -236,6 +236,18 @@ struct drm_atomic_state {
 	struct drm_modeset_acquire_ctx *acquire_ctx;
 
 	/**
+	 * @fake_commit:
+	 *
+	 * Used for signaling unbound planes/connectors.
+	 * When a connector or plane is not bound to any CRTC, it's still important
+	 * to preserve linearity to prevent the atomic states from being freed to early.
+	 *
+	 * This commit (if set) is not bound to any crtc, but will be completed when
+	 * drm_atomic_helper_commit_hw_done() is called.
+	 */
+	struct drm_crtc_commit *fake_commit;
+
+	/**
 	 * @commit_work:
 	 *
 	 * Work item which can be used by the driver or helpers to execute the
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index ea8da401c93c..8837649d16e8 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -347,6 +347,13 @@ struct drm_connector_state {
 
 	struct drm_atomic_state *state;
 
+	/**
+	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
+	 *
+	 * Is only set when @crtc is NULL.
+	 */
+	struct drm_crtc_commit *commit;
+
 	struct drm_tv_connector_state tv;
 
 	/**
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 73f90f9d057f..7d96116fd4c4 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -123,6 +123,13 @@ struct drm_plane_state {
 	 */
 	bool visible;
 
+	/**
+	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
+	 *
+	 * Is only set when @crtc is NULL.
+	 */
+	struct drm_crtc_commit *commit;
+
 	struct drm_atomic_state *state;
 };
 
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
                   ` (3 preceding siblings ...)
  2017-08-30 12:17 ` [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2 Maarten Lankhorst
@ 2017-08-30 12:17 ` Maarten Lankhorst
  2017-08-30 12:46   ` Daniel Vetter
  4 siblings, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:17 UTC (permalink / raw)
  To: dri-devel; +Cc: Gustavo Padovan, intel-gfx

By always keeping track of the last commit in plane_state, we know
whether there is an active update on the plane or not. With that
information we can reject the fast update, and force the slowpath
to be used as was originally intended.

Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
 drivers/gpu/drm/drm_atomic_helper.c  | 25 +++++++++++--------------
 drivers/gpu/drm/i915/intel_display.c |  8 ++++++++
 include/drm/drm_plane.h              |  5 +++--
 3 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 034f563fb130..384d99347bb3 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1388,8 +1388,8 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
 {
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *crtc_state;
-	struct drm_plane *__plane, *plane = NULL;
-	struct drm_plane_state *__plane_state, *plane_state = NULL;
+	struct drm_plane *plane;
+	struct drm_plane_state *old_plane_state, *new_plane_state;
 	const struct drm_plane_helper_funcs *funcs;
 	int i, n_planes = 0;
 
@@ -1398,33 +1398,33 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
 			return -EINVAL;
 	}
 
-	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
+	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
 		n_planes++;
-		plane = __plane;
-		plane_state = __plane_state;
-	}
 
 	/* FIXME: we support only single plane updates for now */
-	if (!plane || n_planes != 1)
+	if (n_planes != 1)
 		return -EINVAL;
 
-	if (!plane_state->crtc)
+	if (!new_plane_state->crtc)
 		return -EINVAL;
 
 	funcs = plane->helper_private;
 	if (!funcs->atomic_async_update)
 		return -EINVAL;
 
-	if (plane_state->fence)
+	if (new_plane_state->fence)
 		return -EINVAL;
 
 	/*
-	 * TODO: Don't do an async update if there is an outstanding commit modifying
+	 * Don't do an async update if there is an outstanding commit modifying
 	 * the plane.  This prevents our async update's changes from getting
 	 * overridden by a previous synchronous update's state.
 	 */
+	if (old_plane_state->commit &&
+	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
+		return -EBUSY;
 
-	return funcs->atomic_async_check(plane, plane_state);
+	return funcs->atomic_async_check(plane, new_plane_state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_async_check);
 
@@ -1790,9 +1790,6 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
 	}
 
 	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
-		if (new_plane_state->crtc)
-			continue;
-
 		if (nonblock && old_plane_state->commit &&
 		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
 			return -EBUSY;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 70ce02753177..cf21ec4ce920 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13043,6 +13043,14 @@ intel_legacy_cursor_update(struct drm_plane *plane,
 		goto slow;
 
 	old_plane_state = plane->state;
+	/*
+	 * Don't do an async update if there is an outstanding commit modifying
+	 * the plane.  This prevents our async update's changes from getting
+	 * overridden by a previous synchronous update's state.
+	 */
+	if (old_plane_state->commit &&
+	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
+		goto slow;
 
 	/*
 	 * If any parameters change that may affect watermarks,
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 7d96116fd4c4..feb9941d0cdb 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -124,9 +124,10 @@ struct drm_plane_state {
 	bool visible;
 
 	/**
-	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
+	 * @commit: Tracks the pending commit to prevent use-after-free conditions,
+	 * and for async plane updates.
 	 *
-	 * Is only set when @crtc is NULL.
+	 * May be NULL.
 	 */
 	struct drm_crtc_commit *commit;
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done
  2017-08-30 12:17 ` [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done Maarten Lankhorst
@ 2017-08-30 12:28   ` Daniel Vetter
  0 siblings, 0 replies; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 12:28 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 02:17:49PM +0200, Maarten Lankhorst wrote:
> When commit synchronization through drm_crtc_commit was first
> introduced, we tried to solve the problem of the flip_done
> needing a reference count by blocking in cleanup_done.
> 
> This has been changed by commit 24835e442f28 ("drm: reference count
> event->completion") which made the waits here no longer needed.

This is wrong. Your next patch makes this no longer necessary.
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 17 -----------------
>  1 file changed, 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 4e53aae9a1fb..11d0e94a2181 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1885,7 +1885,6 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
>  	struct drm_crtc_state *new_crtc_state;
>  	struct drm_crtc_commit *commit;
>  	int i;
> -	long ret;
>  
>  	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
>  		commit = old_state->crtcs[i].commit;
> @@ -1895,22 +1894,6 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
>  		complete_all(&commit->cleanup_done);
>  		WARN_ON(!try_wait_for_completion(&commit->hw_done));
>  
> -		/* commit_list borrows our reference, need to remove before we
> -		 * clean up our drm_atomic_state. But only after it actually
> -		 * completed, otherwise subsequent commits won't stall properly. */

This comment here is the clue: "... otherwise subsequent commits wont
stall properly". When we remove the drm_crtc_commit from commit_list, the
next commit won't be able to properly stall for flip_done. Hence we must
wait for flip_done before removing it.

But with your new patch we wait for flip_done using
old_crtc_state->commit, and this is indeed no longer necessary.

With the commit message and patch ordering fixed up:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> -		if (try_wait_for_completion(&commit->flip_done))
> -			goto del_commit;
> -
> -		/* We must wait for the vblank event to signal our completion
> -		 * before releasing our reference, since the vblank work does
> -		 * not hold a reference of its own. */
> -		ret = wait_for_completion_timeout(&commit->flip_done,
> -						  10*HZ);
> -		if (ret == 0)
> -			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
> -				  crtc->base.id, crtc->name);
> -
> -del_commit:
>  		spin_lock(&crtc->commit_lock);
>  		list_del(&commit->commit_entry);
>  		spin_unlock(&crtc->commit_lock);
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 12:17 ` [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2 Maarten Lankhorst
@ 2017-08-30 12:40   ` Daniel Vetter
  2017-08-30 12:46     ` [Intel-gfx] " Maarten Lankhorst
  2017-08-30 14:10   ` Laurent Pinchart
  1 sibling, 1 reply; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 12:40 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx, Laurent Pinchart, dri-devel

On Wed, Aug 30, 2017 at 02:17:51PM +0200, Maarten Lankhorst wrote:
> Currently we neatly track the crtc state, but forget to look at
> plane/connector state.
> 
> When doing a nonblocking modeset, immediately followed by a setprop
> before the modeset completes, the setprop will see the modesets new
> state as the old state and free it.
> 
> This has to be solved by waiting for hw_done on the connector, even
> if it's not assigned to a crtc. When a connector is unbound we take
> the last crtc commit, and when it stays unbound we create a new
> fake crtc commit for that gets signaled on hw_done for all the
> planes/connectors.
> 
> We wait for it the same way as we do for crtc's, which will make
> sure we never run into a use-after-free situation.
> 
> Changes since v1:
> - Only create a single disable commit. (danvet)
> - Fix leak in intel_legacy_cursor_update.
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/gpu/drm/drm_atomic.c         |   4 +
>  drivers/gpu/drm/drm_atomic_helper.c  | 156 +++++++++++++++++++++++++++++++++--
>  drivers/gpu/drm/i915/intel_display.c |   2 +
>  include/drm/drm_atomic.h             |  12 +++
>  include/drm/drm_connector.h          |   7 ++
>  include/drm/drm_plane.h              |   7 ++
>  6 files changed, 182 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 2cce48f203e0..75f5f74de9bf 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
>  	}
>  	state->num_private_objs = 0;
>  
> +	if (state->fake_commit) {
> +		drm_crtc_commit_put(state->fake_commit);
> +		state->fake_commit = NULL;
> +	}
>  }
>  EXPORT_SYMBOL(drm_atomic_state_default_clear);
>  
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 8ccb8b6536c0..034f563fb130 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion *completion)
>  	drm_crtc_commit_put(commit);
>  }
>  
> +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
> +{
> +	init_completion(&commit->flip_done);
> +	init_completion(&commit->hw_done);
> +	init_completion(&commit->cleanup_done);
> +	INIT_LIST_HEAD(&commit->commit_entry);
> +	kref_init(&commit->ref);
> +	commit->crtc = crtc;
> +}
> +
> +static struct drm_crtc_commit *
> +fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
> +{
> +	struct drm_crtc_commit *commit;
> +
> +	if (crtc) {
> +		struct drm_crtc_state *new_crtc_state;
> +
> +		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +		commit = new_crtc_state->commit;
> +	} else if (!state->fake_commit) {
> +		state->fake_commit = commit = kzalloc(sizeof(*commit), GFP_KERNEL);
> +		if (!commit)
> +			return NULL;
> +
> +		init_commit(commit, NULL);
> +	} else
> +		commit = state->fake_commit;
> +
> +	drm_crtc_commit_get(commit);

Double refcount for the case where you call init_commit? Aka I think this
leaks.

> +	return commit;
> +}
> +
>  /**
>   * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
>   * @state: new modeset state to be committed
> @@ -1692,6 +1726,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>  {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> +	struct drm_connector *conn;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state, *new_plane_state;
>  	struct drm_crtc_commit *commit;
>  	int i, ret;
>  
> @@ -1700,12 +1738,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>  		if (!commit)
>  			return -ENOMEM;
>  
> -		init_completion(&commit->flip_done);
> -		init_completion(&commit->hw_done);
> -		init_completion(&commit->cleanup_done);
> -		INIT_LIST_HEAD(&commit->commit_entry);
> -		kref_init(&commit->ref);
> -		commit->crtc = crtc;
> +		init_commit(commit, crtc);
>  
>  		new_crtc_state->commit = commit;
>  
> @@ -1741,6 +1774,36 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>  		drm_crtc_commit_get(commit);
>  	}
>  
> +	for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {

Maybe a commen there like
		/* commit tracked through the crtc_state->commit */
Feels at least a bit non-obvious how this works.

> +		if (new_conn_state->crtc)
> +			continue;
> +

Please add the

			/* Userspace is not allowed to get ahead of the previous
			 * commit with nonblocking ones. */

comment from stall_checks here and below. Even better if we could extract
this, but macro is ugly and functions wont work.

> +		if (nonblock && old_conn_state->commit &&
> +		    !try_wait_for_completion(&old_conn_state->commit->flip_done))
> +			return -EBUSY;
> +
> +		commit = fake_or_crtc_commit(state, old_conn_state->crtc);
> +		if (!commit)
> +			return -ENOMEM;
> +
> +		new_conn_state->commit = commit;
> +	}
> +
> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
> +		if (new_plane_state->crtc)
> +			continue;
> +
> +		if (nonblock && old_plane_state->commit &&
> +		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
> +			return -EBUSY;
> +
> +		commit = fake_or_crtc_commit(state, old_plane_state->crtc);
> +		if (!commit)
> +			return -ENOMEM;
> +
> +		new_plane_state->commit = commit;
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
> @@ -1761,6 +1824,10 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
>  {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state;
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state;
> +	struct drm_connector *conn;
> +	struct drm_connector_state *old_conn_state;
>  	struct drm_crtc_commit *commit;
>  	int i;
>  	long ret;
> @@ -1785,6 +1852,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
>  			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
>  				  crtc->base.id, crtc->name);
>  	}
> +
> +	for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
> +		commit = old_conn_state->commit;
> +
> +		if (!commit)
> +			continue;
> +
> +		ret = wait_for_completion_timeout(&commit->hw_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
> +				  conn->base.id, conn->name);
> +
> +		/* Currently no support for overwriting flips, hence
> +		 * stall for previous one to execute completely. */
> +		ret = wait_for_completion_timeout(&commit->flip_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
> +				  conn->base.id, conn->name);
> +	}
> +
> +	for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
> +		commit = old_plane_state->commit;
> +
> +		if (!commit)
> +			continue;
> +
> +		ret = wait_for_completion_timeout(&commit->hw_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
> +				  plane->base.id, plane->name);
> +
> +		/* Currently no support for overwriting flips, hence
> +		 * stall for previous one to execute completely. */
> +		ret = wait_for_completion_timeout(&commit->flip_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
> +				  plane->base.id, plane->name);
> +	}
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
>  
> @@ -1819,6 +1928,11 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
>  		WARN_ON(new_crtc_state->event);
>  		complete_all(&commit->hw_done);
>  	}
> +
> +	if (old_state->fake_commit) {
> +		complete_all(&old_state->fake_commit->hw_done);
> +		complete_all(&old_state->fake_commit->flip_done);
> +	}

Just to avoid surprises, I think we should also complete_all(cleanup_done)
on the fake commit in cleanup_done().

>  }
>  EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
>  
> @@ -2241,6 +2355,28 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
>  			if (ret)
>  				return ret;
>  		}
> +
> +		for_each_old_connector_in_state(state, connector, old_conn_state, i) {
> +			commit = old_conn_state->commit;
> +
> +			if (!commit)
> +				continue;
> +
> +			ret = wait_for_completion_interruptible(&commit->hw_done);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		for_each_old_plane_in_state(state, plane, old_plane_state, i) {
> +			commit = old_plane_state->commit;
> +
> +			if (!commit)
> +				continue;
> +
> +			ret = wait_for_completion_interruptible(&commit->hw_done);
> +			if (ret)
> +				return ret;
> +		}
>  	}
>  
>  	for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) {
> @@ -3223,6 +3359,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
>  		drm_framebuffer_get(state->fb);
>  
>  	state->fence = NULL;
> +	state->commit = NULL;
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
>  
> @@ -3264,6 +3401,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
>  
>  	if (state->fence)
>  		dma_fence_put(state->fence);
> +
> +	if (state->commit)
> +		drm_crtc_commit_put(state->commit);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
>  
> @@ -3342,6 +3482,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
>  	memcpy(state, connector->state, sizeof(*state));
>  	if (state->crtc)
>  		drm_connector_get(connector);
> +	state->commit = NULL;
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
>  
> @@ -3468,6 +3609,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
>  {
>  	if (state->crtc)
>  		drm_connector_put(state->connector);
> +
> +	if (state->commit)
> +		drm_crtc_commit_put(state->commit);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 3f3cb96aa11e..70ce02753177 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -13109,8 +13109,10 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>  
>  	/* Swap plane state */
>  	new_plane_state->fence = old_plane_state->fence;
> +	new_plane_state->commit = old_plane_state->commit;
>  	*to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state);
>  	new_plane_state->fence = NULL;
> +	new_plane_state->commit = NULL;

Would be good if we could switch to Gustavo's async work and push these
tricks into shared code ...

>  	new_plane_state->fb = old_fb;
>  	to_intel_plane_state(new_plane_state)->vma = NULL;
>  
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index e76d9f95c191..ba976f4a4938 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -236,6 +236,18 @@ struct drm_atomic_state {
>  	struct drm_modeset_acquire_ctx *acquire_ctx;
>  
>  	/**
> +	 * @fake_commit:
> +	 *
> +	 * Used for signaling unbound planes/connectors.
> +	 * When a connector or plane is not bound to any CRTC, it's still important
> +	 * to preserve linearity to prevent the atomic states from being freed to early.

s/to early/too early/.

> +	 *
> +	 * This commit (if set) is not bound to any crtc, but will be completed when
> +	 * drm_atomic_helper_commit_hw_done() is called.

... and drm_atomic_helper_cleanup_done() ...


> +	 */
> +	struct drm_crtc_commit *fake_commit;
> +
> +	/**
>  	 * @commit_work:
>  	 *
>  	 * Work item which can be used by the driver or helpers to execute the
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index ea8da401c93c..8837649d16e8 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -347,6 +347,13 @@ struct drm_connector_state {
>  
>  	struct drm_atomic_state *state;
>  
> +	/**
> +	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
> +	 *
> +	 * Is only set when @crtc is NULL.
> +	 */
> +	struct drm_crtc_commit *commit;
> +
>  	struct drm_tv_connector_state tv;
>  
>  	/**
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 73f90f9d057f..7d96116fd4c4 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -123,6 +123,13 @@ struct drm_plane_state {
>  	 */
>  	bool visible;
>  
> +	/**
> +	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
> +	 *
> +	 * Is only set when @crtc is NULL.
> +	 */
> +	struct drm_crtc_commit *commit;
> +
>  	struct drm_atomic_state *state;
>  };

With the above nits addressed:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

>  
> -- 
> 2.11.0
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
@ 2017-08-30 12:43   ` Daniel Vetter
  2017-08-30 12:54     ` Maarten Lankhorst
  2017-09-04 11:18   ` [Intel-gfx] " Chris Wilson
  1 sibling, 1 reply; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 12:43 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
> The next commit removes the wait for flip_done in in
> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
> to pass. Instead of using complicated vblank tracking which ends
> up being ignored anyway, call the correct atomic helper. :)
> 
> RFC because I'm not completely sure what we want to do with the vblank waiting,
> I think for now this patch is the right way to go until we decide because it
> preserves the status quo when drm_crtc_commit was introduced.
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
>  2 files changed, 8 insertions(+), 78 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index cbbafbfb0a55..de19621864a9 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
>  			    struct drm_atomic_state *old_state);
>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
>  			     struct drm_atomic_state *old_state);
> -	void (*update_crtcs)(struct drm_atomic_state *state,
> -			     unsigned int *crtc_vblank_mask);
> +	void (*update_crtcs)(struct drm_atomic_state *state);
>  	void (*audio_codec_enable)(struct drm_connector *connector,
>  				   struct intel_encoder *encoder,
>  				   const struct drm_display_mode *adjusted_mode);
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 52c73b4dabaa..3f3cb96aa11e 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
>  }
>  
> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
> -					  struct drm_i915_private *dev_priv,
> -					  unsigned crtc_mask)
> -{
> -	unsigned last_vblank_count[I915_MAX_PIPES];
> -	enum pipe pipe;
> -	int ret;
> -
> -	if (!crtc_mask)
> -		return;
> -
> -	for_each_pipe(dev_priv, pipe) {
> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> -								  pipe);
> -
> -		if (!((1 << pipe) & crtc_mask))
> -			continue;
> -
> -		ret = drm_crtc_vblank_get(&crtc->base);
> -		if (WARN_ON(ret != 0)) {
> -			crtc_mask &= ~(1 << pipe);
> -			continue;
> -		}
> -
> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
> -	}
> -
> -	for_each_pipe(dev_priv, pipe) {
> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> -								  pipe);
> -		long lret;
> -
> -		if (!((1 << pipe) & crtc_mask))
> -			continue;
> -
> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
> -				last_vblank_count[pipe] !=
> -					drm_crtc_vblank_count(&crtc->base),
> -				msecs_to_jiffies(50));
> -
> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
> -
> -		drm_crtc_vblank_put(&crtc->base);
> -	}
> -}
> -
> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
> -{
> -	/* fb updated, need to unpin old fb */
> -	if (crtc_state->fb_changed)
> -		return true;
> -
> -	/* wm changes, need vblank before final wm's */
> -	if (crtc_state->update_wm_post)
> -		return true;
> -
> -	if (crtc_state->wm.need_postvbl_update)
> -		return true;
> -
> -	return false;
> -}
> -
>  static void intel_update_crtc(struct drm_crtc *crtc,
>  			      struct drm_atomic_state *state,
>  			      struct drm_crtc_state *old_crtc_state,
> -			      struct drm_crtc_state *new_crtc_state,
> -			      unsigned int *crtc_vblank_mask)
> +			      struct drm_crtc_state *new_crtc_state)
>  {
>  	struct drm_device *dev = crtc->dev;
>  	struct drm_i915_private *dev_priv = to_i915(dev);
> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
>  	}
>  
>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
> -
> -	if (needs_vblank_wait(pipe_config))
> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
>  }
>  
> -static void intel_update_crtcs(struct drm_atomic_state *state,
> -			       unsigned int *crtc_vblank_mask)
> +static void intel_update_crtcs(struct drm_atomic_state *state)
>  {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
>  			continue;
>  
>  		intel_update_crtc(crtc, state, old_crtc_state,
> -				  new_crtc_state, crtc_vblank_mask);
> +				  new_crtc_state);
>  	}
>  }
>  
> -static void skl_update_crtcs(struct drm_atomic_state *state,
> -			     unsigned int *crtc_vblank_mask)
> +static void skl_update_crtcs(struct drm_atomic_state *state)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
>  				vbl_wait = true;
>  
>  			intel_update_crtc(crtc, state, old_crtc_state,
> -					  new_crtc_state, crtc_vblank_mask);
> +					  new_crtc_state);
>  
>  			if (vbl_wait)
>  				intel_wait_for_vblank(dev_priv, pipe);
> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  	struct intel_crtc_state *intel_cstate;
>  	bool hw_check = intel_state->modeset;
>  	u64 put_domains[I915_MAX_PIPES] = {};
> -	unsigned crtc_vblank_mask = 0;
>  	int i;
>  
>  	intel_atomic_commit_fence_wait(intel_state);
> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
>  
>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
> +	dev_priv->display.update_crtcs(state);
>  
>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>  	 * already, but still need the state for the delayed optimization. To
> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>  	 *   we don't need out special handling any more.
>  	 */
>  	if (!state->legacy_cursor_update)

I think this if is dead code, since either we use the fastpath in
intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
then we clear this hack (on most platforms at least) since it just wreaks
complete havoc.

We should probably do this logic in the async helpers, i.e. if async
helpers reject a cursor update, clear state->legacy_cursor_update.

Since I think we don't necessarily need this patch in this series, I think
it'd be better in a separate series to straighten out the async_update
story. Which would then include moving i915 over to the core support.
-Daniel

> -		intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
> +		drm_atomic_helper_wait_for_flip_done(dev, state);
>  
>  	/*
>  	 * Now that the vblank has passed, we can go ahead and program the
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [Intel-gfx] [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 12:40   ` Daniel Vetter
@ 2017-08-30 12:46     ` Maarten Lankhorst
  0 siblings, 0 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, Laurent Pinchart, dri-devel

Op 30-08-17 om 14:40 schreef Daniel Vetter:
> On Wed, Aug 30, 2017 at 02:17:51PM +0200, Maarten Lankhorst wrote:
>> Currently we neatly track the crtc state, but forget to look at
>> plane/connector state.
>>
>> When doing a nonblocking modeset, immediately followed by a setprop
>> before the modeset completes, the setprop will see the modesets new
>> state as the old state and free it.
>>
>> This has to be solved by waiting for hw_done on the connector, even
>> if it's not assigned to a crtc. When a connector is unbound we take
>> the last crtc commit, and when it stays unbound we create a new
>> fake crtc commit for that gets signaled on hw_done for all the
>> planes/connectors.
>>
>> We wait for it the same way as we do for crtc's, which will make
>> sure we never run into a use-after-free situation.
>>
>> Changes since v1:
>> - Only create a single disable commit. (danvet)
>> - Fix leak in intel_legacy_cursor_update.
>>
>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
>> Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
>> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> ---
>>  drivers/gpu/drm/drm_atomic.c         |   4 +
>>  drivers/gpu/drm/drm_atomic_helper.c  | 156 +++++++++++++++++++++++++++++++++--
>>  drivers/gpu/drm/i915/intel_display.c |   2 +
>>  include/drm/drm_atomic.h             |  12 +++
>>  include/drm/drm_connector.h          |   7 ++
>>  include/drm/drm_plane.h              |   7 ++
>>  6 files changed, 182 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
>> index 2cce48f203e0..75f5f74de9bf 100644
>> --- a/drivers/gpu/drm/drm_atomic.c
>> +++ b/drivers/gpu/drm/drm_atomic.c
>> @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
>>  	}
>>  	state->num_private_objs = 0;
>>  
>> +	if (state->fake_commit) {
>> +		drm_crtc_commit_put(state->fake_commit);
>> +		state->fake_commit = NULL;
>> +	}
>>  }
>>  EXPORT_SYMBOL(drm_atomic_state_default_clear);
>>  
>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
>> index 8ccb8b6536c0..034f563fb130 100644
>> --- a/drivers/gpu/drm/drm_atomic_helper.c
>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
>> @@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion *completion)
>>  	drm_crtc_commit_put(commit);
>>  }
>>  
>> +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
>> +{
>> +	init_completion(&commit->flip_done);
>> +	init_completion(&commit->hw_done);
>> +	init_completion(&commit->cleanup_done);
>> +	INIT_LIST_HEAD(&commit->commit_entry);
>> +	kref_init(&commit->ref);
>> +	commit->crtc = crtc;
>> +}
>> +
>> +static struct drm_crtc_commit *
>> +fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
>> +{
>> +	struct drm_crtc_commit *commit;
>> +
>> +	if (crtc) {
>> +		struct drm_crtc_state *new_crtc_state;
>> +
>> +		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
>> +
>> +		commit = new_crtc_state->commit;
>> +	} else if (!state->fake_commit) {
>> +		state->fake_commit = commit = kzalloc(sizeof(*commit), GFP_KERNEL);
>> +		if (!commit)
>> +			return NULL;
>> +
>> +		init_commit(commit, NULL);
>> +	} else
>> +		commit = state->fake_commit;
>> +
>> +	drm_crtc_commit_get(commit);
> Double refcount for the case where you call init_commit? Aka I think this
> leaks.
>
>> +	return commit;
>> +}
>> +
>>  /**
>>   * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
>>   * @state: new modeset state to be committed
>> @@ -1692,6 +1726,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>>  {
>>  	struct drm_crtc *crtc;
>>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>> +	struct drm_connector *conn;
>> +	struct drm_connector_state *old_conn_state, *new_conn_state;
>> +	struct drm_plane *plane;
>> +	struct drm_plane_state *old_plane_state, *new_plane_state;
>>  	struct drm_crtc_commit *commit;
>>  	int i, ret;
>>  
>> @@ -1700,12 +1738,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>>  		if (!commit)
>>  			return -ENOMEM;
>>  
>> -		init_completion(&commit->flip_done);
>> -		init_completion(&commit->hw_done);
>> -		init_completion(&commit->cleanup_done);
>> -		INIT_LIST_HEAD(&commit->commit_entry);
>> -		kref_init(&commit->ref);
>> -		commit->crtc = crtc;
>> +		init_commit(commit, crtc);
>>  
>>  		new_crtc_state->commit = commit;
>>  
>> @@ -1741,6 +1774,36 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>>  		drm_crtc_commit_get(commit);
>>  	}
>>  
>> +	for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
> Maybe a commen there like
> 		/* commit tracked through the crtc_state->commit */
> Feels at least a bit non-obvious how this works.
>
>> +		if (new_conn_state->crtc)
>> +			continue;
>> +
> Please add the
>
> 			/* Userspace is not allowed to get ahead of the previous
> 			 * commit with nonblocking ones. */
>
> comment from stall_checks here and below. Even better if we could extract
> this, but macro is ugly and functions wont work.
Ok.
>> +		if (nonblock && old_conn_state->commit &&
>> +		    !try_wait_for_completion(&old_conn_state->commit->flip_done))
>> +			return -EBUSY;
>> +
>> +		commit = fake_or_crtc_commit(state, old_conn_state->crtc);
>> +		if (!commit)
>> +			return -ENOMEM;
>> +
>> +		new_conn_state->commit = commit;
>> +	}
>> +
>> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
>> +		if (new_plane_state->crtc)
>> +			continue;
>> +
>> +		if (nonblock && old_plane_state->commit &&
>> +		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
>> +			return -EBUSY;
>> +
>> +		commit = fake_or_crtc_commit(state, old_plane_state->crtc);
>> +		if (!commit)
>> +			return -ENOMEM;
>> +
>> +		new_plane_state->commit = commit;
>> +	}
>> +
>>  	return 0;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
>> @@ -1761,6 +1824,10 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
>>  {
>>  	struct drm_crtc *crtc;
>>  	struct drm_crtc_state *old_crtc_state;
>> +	struct drm_plane *plane;
>> +	struct drm_plane_state *old_plane_state;
>> +	struct drm_connector *conn;
>> +	struct drm_connector_state *old_conn_state;
>>  	struct drm_crtc_commit *commit;
>>  	int i;
>>  	long ret;
>> @@ -1785,6 +1852,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
>>  			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
>>  				  crtc->base.id, crtc->name);
>>  	}
>> +
>> +	for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
>> +		commit = old_conn_state->commit;
>> +
>> +		if (!commit)
>> +			continue;
>> +
>> +		ret = wait_for_completion_timeout(&commit->hw_done,
>> +						  10*HZ);
>> +		if (ret == 0)
>> +			DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
>> +				  conn->base.id, conn->name);
>> +
>> +		/* Currently no support for overwriting flips, hence
>> +		 * stall for previous one to execute completely. */
>> +		ret = wait_for_completion_timeout(&commit->flip_done,
>> +						  10*HZ);
>> +		if (ret == 0)
>> +			DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
>> +				  conn->base.id, conn->name);
>> +	}
>> +
>> +	for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
>> +		commit = old_plane_state->commit;
>> +
>> +		if (!commit)
>> +			continue;
>> +
>> +		ret = wait_for_completion_timeout(&commit->hw_done,
>> +						  10*HZ);
>> +		if (ret == 0)
>> +			DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
>> +				  plane->base.id, plane->name);
>> +
>> +		/* Currently no support for overwriting flips, hence
>> +		 * stall for previous one to execute completely. */
>> +		ret = wait_for_completion_timeout(&commit->flip_done,
>> +						  10*HZ);
>> +		if (ret == 0)
>> +			DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
>> +				  plane->base.id, plane->name);
>> +	}
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
>>  
>> @@ -1819,6 +1928,11 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
>>  		WARN_ON(new_crtc_state->event);
>>  		complete_all(&commit->hw_done);
>>  	}
>> +
>> +	if (old_state->fake_commit) {
>> +		complete_all(&old_state->fake_commit->hw_done);
>> +		complete_all(&old_state->fake_commit->flip_done);
>> +	}
> Just to avoid surprises, I think we should also complete_all(cleanup_done)
> on the fake commit in cleanup_done().
Sure, now there's only 1 it makes sense. :)
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
>>  
>> @@ -2241,6 +2355,28 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
>>  			if (ret)
>>  				return ret;
>>  		}
>> +
>> +		for_each_old_connector_in_state(state, connector, old_conn_state, i) {
>> +			commit = old_conn_state->commit;
>> +
>> +			if (!commit)
>> +				continue;
>> +
>> +			ret = wait_for_completion_interruptible(&commit->hw_done);
>> +			if (ret)
>> +				return ret;
>> +		}
>> +
>> +		for_each_old_plane_in_state(state, plane, old_plane_state, i) {
>> +			commit = old_plane_state->commit;
>> +
>> +			if (!commit)
>> +				continue;
>> +
>> +			ret = wait_for_completion_interruptible(&commit->hw_done);
>> +			if (ret)
>> +				return ret;
>> +		}
>>  	}
>>  
>>  	for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) {
>> @@ -3223,6 +3359,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
>>  		drm_framebuffer_get(state->fb);
>>  
>>  	state->fence = NULL;
>> +	state->commit = NULL;
>>  }
>>  EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
>>  
>> @@ -3264,6 +3401,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
>>  
>>  	if (state->fence)
>>  		dma_fence_put(state->fence);
>> +
>> +	if (state->commit)
>> +		drm_crtc_commit_put(state->commit);
>>  }
>>  EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
>>  
>> @@ -3342,6 +3482,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
>>  	memcpy(state, connector->state, sizeof(*state));
>>  	if (state->crtc)
>>  		drm_connector_get(connector);
>> +	state->commit = NULL;
>>  }
>>  EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
>>  
>> @@ -3468,6 +3609,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
>>  {
>>  	if (state->crtc)
>>  		drm_connector_put(state->connector);
>> +
>> +	if (state->commit)
>> +		drm_crtc_commit_put(state->commit);
>>  }
>>  EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
>>  
>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>> index 3f3cb96aa11e..70ce02753177 100644
>> --- a/drivers/gpu/drm/i915/intel_display.c
>> +++ b/drivers/gpu/drm/i915/intel_display.c
>> @@ -13109,8 +13109,10 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>>  
>>  	/* Swap plane state */
>>  	new_plane_state->fence = old_plane_state->fence;
>> +	new_plane_state->commit = old_plane_state->commit;
>>  	*to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state);
>>  	new_plane_state->fence = NULL;
>> +	new_plane_state->commit = NULL;
> Would be good if we could switch to Gustavo's async work and push these
> tricks into shared code ...
We do this as an evil hack because we can't track whether something is using the current plane or not.

If we apply patch 5, we could directly swap plane->state because we know whether the cursor plane is in use or not,
and get rid of this evil hack here.
>
>>  	new_plane_state->fb = old_fb;
>>  	to_intel_plane_state(new_plane_state)->vma = NULL;
>>  
>> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
>> index e76d9f95c191..ba976f4a4938 100644
>> --- a/include/drm/drm_atomic.h
>> +++ b/include/drm/drm_atomic.h
>> @@ -236,6 +236,18 @@ struct drm_atomic_state {
>>  	struct drm_modeset_acquire_ctx *acquire_ctx;
>>  
>>  	/**
>> +	 * @fake_commit:
>> +	 *
>> +	 * Used for signaling unbound planes/connectors.
>> +	 * When a connector or plane is not bound to any CRTC, it's still important
>> +	 * to preserve linearity to prevent the atomic states from being freed to early.
> s/to early/too early/.
>
>> +	 *
>> +	 * This commit (if set) is not bound to any crtc, but will be completed when
>> +	 * drm_atomic_helper_commit_hw_done() is called.
> ... and drm_atomic_helper_cleanup_done() ...
>
>
>> +	 */
>> +	struct drm_crtc_commit *fake_commit;
>> +
>> +	/**
>>  	 * @commit_work:
>>  	 *
>>  	 * Work item which can be used by the driver or helpers to execute the
>> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
>> index ea8da401c93c..8837649d16e8 100644
>> --- a/include/drm/drm_connector.h
>> +++ b/include/drm/drm_connector.h
>> @@ -347,6 +347,13 @@ struct drm_connector_state {
>>  
>>  	struct drm_atomic_state *state;
>>  
>> +	/**
>> +	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
>> +	 *
>> +	 * Is only set when @crtc is NULL.
>> +	 */
>> +	struct drm_crtc_commit *commit;
>> +
>>  	struct drm_tv_connector_state tv;
>>  
>>  	/**
>> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
>> index 73f90f9d057f..7d96116fd4c4 100644
>> --- a/include/drm/drm_plane.h
>> +++ b/include/drm/drm_plane.h
>> @@ -123,6 +123,13 @@ struct drm_plane_state {
>>  	 */
>>  	bool visible;
>>  
>> +	/**
>> +	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
>> +	 *
>> +	 * Is only set when @crtc is NULL.
>> +	 */
>> +	struct drm_crtc_commit *commit;
>> +
>>  	struct drm_atomic_state *state;
>>  };
> With the above nits addressed:
>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-08-30 12:17 ` [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended Maarten Lankhorst
@ 2017-08-30 12:46   ` Daniel Vetter
  2017-08-30 12:53     ` Maarten Lankhorst
  2017-09-02 18:59     ` [Intel-gfx] " Gustavo Padovan
  0 siblings, 2 replies; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 12:46 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: Gustavo Padovan, intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 02:17:52PM +0200, Maarten Lankhorst wrote:
> By always keeping track of the last commit in plane_state, we know
> whether there is an active update on the plane or not. With that
> information we can reject the fast update, and force the slowpath
> to be used as was originally intended.
> 
> Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>

Makes sense, but I think like patch 1 it would be better to do this in a
separate series. Which would then include a patch to move i915 over to the
async plane support.

One more comment below.

> ---
>  drivers/gpu/drm/drm_atomic_helper.c  | 25 +++++++++++--------------
>  drivers/gpu/drm/i915/intel_display.c |  8 ++++++++
>  include/drm/drm_plane.h              |  5 +++--
>  3 files changed, 22 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 034f563fb130..384d99347bb3 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1388,8 +1388,8 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
>  {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *crtc_state;
> -	struct drm_plane *__plane, *plane = NULL;
> -	struct drm_plane_state *__plane_state, *plane_state = NULL;
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state, *new_plane_state;
>  	const struct drm_plane_helper_funcs *funcs;
>  	int i, n_planes = 0;
>  
> @@ -1398,33 +1398,33 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
>  			return -EINVAL;
>  	}
>  
> -	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
>  		n_planes++;
> -		plane = __plane;
> -		plane_state = __plane_state;
> -	}
>  
>  	/* FIXME: we support only single plane updates for now */
> -	if (!plane || n_planes != 1)
> +	if (n_planes != 1)
>  		return -EINVAL;
>  
> -	if (!plane_state->crtc)
> +	if (!new_plane_state->crtc)
>  		return -EINVAL;
>  
>  	funcs = plane->helper_private;
>  	if (!funcs->atomic_async_update)
>  		return -EINVAL;
>  
> -	if (plane_state->fence)
> +	if (new_plane_state->fence)
>  		return -EINVAL;
>  
>  	/*
> -	 * TODO: Don't do an async update if there is an outstanding commit modifying
> +	 * Don't do an async update if there is an outstanding commit modifying
>  	 * the plane.  This prevents our async update's changes from getting
>  	 * overridden by a previous synchronous update's state.
>  	 */
> +	if (old_plane_state->commit &&
> +	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
> +		return -EBUSY;

Instead of forcing us to always set the plane_state->commit pointer (bunch
of pointles refcounting), perhaps just check
plane_state->crtc->state->commit? We do hold the necessary locks to at
least look at that.
-Daniel

>  
> -	return funcs->atomic_async_check(plane, plane_state);
> +	return funcs->atomic_async_check(plane, new_plane_state);
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_async_check);
>  
> @@ -1790,9 +1790,6 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
>  	}
>  
>  	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
> -		if (new_plane_state->crtc)
> -			continue;
> -
>  		if (nonblock && old_plane_state->commit &&
>  		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
>  			return -EBUSY;
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 70ce02753177..cf21ec4ce920 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -13043,6 +13043,14 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>  		goto slow;
>  
>  	old_plane_state = plane->state;
> +	/*
> +	 * Don't do an async update if there is an outstanding commit modifying
> +	 * the plane.  This prevents our async update's changes from getting
> +	 * overridden by a previous synchronous update's state.
> +	 */
> +	if (old_plane_state->commit &&
> +	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
> +		goto slow;
>  
>  	/*
>  	 * If any parameters change that may affect watermarks,
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 7d96116fd4c4..feb9941d0cdb 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -124,9 +124,10 @@ struct drm_plane_state {
>  	bool visible;
>  
>  	/**
> -	 * @commit: Tracks the pending commit to prevent use-after-free conditions.
> +	 * @commit: Tracks the pending commit to prevent use-after-free conditions,
> +	 * and for async plane updates.
>  	 *
> -	 * Is only set when @crtc is NULL.
> +	 * May be NULL.
>  	 */
>  	struct drm_crtc_commit *commit;
>  
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-08-30 12:46   ` Daniel Vetter
@ 2017-08-30 12:53     ` Maarten Lankhorst
  2017-08-30 13:43       ` Daniel Vetter
  2017-09-02 18:59     ` [Intel-gfx] " Gustavo Padovan
  1 sibling, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:53 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Gustavo Padovan, intel-gfx, dri-devel

Op 30-08-17 om 14:46 schreef Daniel Vetter:
> On Wed, Aug 30, 2017 at 02:17:52PM +0200, Maarten Lankhorst wrote:
>> By always keeping track of the last commit in plane_state, we know
>> whether there is an active update on the plane or not. With that
>> information we can reject the fast update, and force the slowpath
>> to be used as was originally intended.
>>
>> Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Makes sense, but I think like patch 1 it would be better to do this in a
> separate series. Which would then include a patch to move i915 over to the
> async plane support.
>
> One more comment below.
>
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c  | 25 +++++++++++--------------
>>  drivers/gpu/drm/i915/intel_display.c |  8 ++++++++
>>  include/drm/drm_plane.h              |  5 +++--
>>  3 files changed, 22 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
>> index 034f563fb130..384d99347bb3 100644
>> --- a/drivers/gpu/drm/drm_atomic_helper.c
>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
>> @@ -1388,8 +1388,8 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
>>  {
>>  	struct drm_crtc *crtc;
>>  	struct drm_crtc_state *crtc_state;
>> -	struct drm_plane *__plane, *plane = NULL;
>> -	struct drm_plane_state *__plane_state, *plane_state = NULL;
>> +	struct drm_plane *plane;
>> +	struct drm_plane_state *old_plane_state, *new_plane_state;
>>  	const struct drm_plane_helper_funcs *funcs;
>>  	int i, n_planes = 0;
>>  
>> @@ -1398,33 +1398,33 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
>>  			return -EINVAL;
>>  	}
>>  
>> -	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
>> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
>>  		n_planes++;
>> -		plane = __plane;
>> -		plane_state = __plane_state;
>> -	}
>>  
>>  	/* FIXME: we support only single plane updates for now */
>> -	if (!plane || n_planes != 1)
>> +	if (n_planes != 1)
>>  		return -EINVAL;
>>  
>> -	if (!plane_state->crtc)
>> +	if (!new_plane_state->crtc)
>>  		return -EINVAL;
>>  
>>  	funcs = plane->helper_private;
>>  	if (!funcs->atomic_async_update)
>>  		return -EINVAL;
>>  
>> -	if (plane_state->fence)
>> +	if (new_plane_state->fence)
>>  		return -EINVAL;
>>  
>>  	/*
>> -	 * TODO: Don't do an async update if there is an outstanding commit modifying
>> +	 * Don't do an async update if there is an outstanding commit modifying
>>  	 * the plane.  This prevents our async update's changes from getting
>>  	 * overridden by a previous synchronous update's state.
>>  	 */
>> +	if (old_plane_state->commit &&
>> +	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
>> +		return -EBUSY;
> Instead of forcing us to always set the plane_state->commit pointer (bunch
> of pointles refcounting), perhaps just check
> plane_state->crtc->state->commit? We do hold the necessary locks to at
> least look at that.
Then we'd always take the slowpath?

The point here was to check whether the current plane was part of the most recent commit, to know this we must either add a flip_planes mask member to drm_crtc_commit, or add a pointer in plane_state to the most recent commit it was part of.
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 12:43   ` Daniel Vetter
@ 2017-08-30 12:54     ` Maarten Lankhorst
  2017-08-30 13:40       ` Daniel Vetter
  0 siblings, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 12:54 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

Op 30-08-17 om 14:43 schreef Daniel Vetter:
> On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
>> The next commit removes the wait for flip_done in in
>> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
>> to pass. Instead of using complicated vblank tracking which ends
>> up being ignored anyway, call the correct atomic helper. :)
>>
>> RFC because I'm not completely sure what we want to do with the vblank waiting,
>> I think for now this patch is the right way to go until we decide because it
>> preserves the status quo when drm_crtc_commit was introduced.
>>
>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
>> ---
>>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
>>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
>>  2 files changed, 8 insertions(+), 78 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>> index cbbafbfb0a55..de19621864a9 100644
>> --- a/drivers/gpu/drm/i915/i915_drv.h
>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
>>  			    struct drm_atomic_state *old_state);
>>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
>>  			     struct drm_atomic_state *old_state);
>> -	void (*update_crtcs)(struct drm_atomic_state *state,
>> -			     unsigned int *crtc_vblank_mask);
>> +	void (*update_crtcs)(struct drm_atomic_state *state);
>>  	void (*audio_codec_enable)(struct drm_connector *connector,
>>  				   struct intel_encoder *encoder,
>>  				   const struct drm_display_mode *adjusted_mode);
>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>> index 52c73b4dabaa..3f3cb96aa11e 100644
>> --- a/drivers/gpu/drm/i915/intel_display.c
>> +++ b/drivers/gpu/drm/i915/intel_display.c
>> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
>>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
>>  }
>>  
>> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
>> -					  struct drm_i915_private *dev_priv,
>> -					  unsigned crtc_mask)
>> -{
>> -	unsigned last_vblank_count[I915_MAX_PIPES];
>> -	enum pipe pipe;
>> -	int ret;
>> -
>> -	if (!crtc_mask)
>> -		return;
>> -
>> -	for_each_pipe(dev_priv, pipe) {
>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>> -								  pipe);
>> -
>> -		if (!((1 << pipe) & crtc_mask))
>> -			continue;
>> -
>> -		ret = drm_crtc_vblank_get(&crtc->base);
>> -		if (WARN_ON(ret != 0)) {
>> -			crtc_mask &= ~(1 << pipe);
>> -			continue;
>> -		}
>> -
>> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
>> -	}
>> -
>> -	for_each_pipe(dev_priv, pipe) {
>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>> -								  pipe);
>> -		long lret;
>> -
>> -		if (!((1 << pipe) & crtc_mask))
>> -			continue;
>> -
>> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
>> -				last_vblank_count[pipe] !=
>> -					drm_crtc_vblank_count(&crtc->base),
>> -				msecs_to_jiffies(50));
>> -
>> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
>> -
>> -		drm_crtc_vblank_put(&crtc->base);
>> -	}
>> -}
>> -
>> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
>> -{
>> -	/* fb updated, need to unpin old fb */
>> -	if (crtc_state->fb_changed)
>> -		return true;
>> -
>> -	/* wm changes, need vblank before final wm's */
>> -	if (crtc_state->update_wm_post)
>> -		return true;
>> -
>> -	if (crtc_state->wm.need_postvbl_update)
>> -		return true;
>> -
>> -	return false;
>> -}
>> -
>>  static void intel_update_crtc(struct drm_crtc *crtc,
>>  			      struct drm_atomic_state *state,
>>  			      struct drm_crtc_state *old_crtc_state,
>> -			      struct drm_crtc_state *new_crtc_state,
>> -			      unsigned int *crtc_vblank_mask)
>> +			      struct drm_crtc_state *new_crtc_state)
>>  {
>>  	struct drm_device *dev = crtc->dev;
>>  	struct drm_i915_private *dev_priv = to_i915(dev);
>> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
>>  	}
>>  
>>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
>> -
>> -	if (needs_vblank_wait(pipe_config))
>> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
>>  }
>>  
>> -static void intel_update_crtcs(struct drm_atomic_state *state,
>> -			       unsigned int *crtc_vblank_mask)
>> +static void intel_update_crtcs(struct drm_atomic_state *state)
>>  {
>>  	struct drm_crtc *crtc;
>>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
>>  			continue;
>>  
>>  		intel_update_crtc(crtc, state, old_crtc_state,
>> -				  new_crtc_state, crtc_vblank_mask);
>> +				  new_crtc_state);
>>  	}
>>  }
>>  
>> -static void skl_update_crtcs(struct drm_atomic_state *state,
>> -			     unsigned int *crtc_vblank_mask)
>> +static void skl_update_crtcs(struct drm_atomic_state *state)
>>  {
>>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
>>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
>> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
>>  				vbl_wait = true;
>>  
>>  			intel_update_crtc(crtc, state, old_crtc_state,
>> -					  new_crtc_state, crtc_vblank_mask);
>> +					  new_crtc_state);
>>  
>>  			if (vbl_wait)
>>  				intel_wait_for_vblank(dev_priv, pipe);
>> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>  	struct intel_crtc_state *intel_cstate;
>>  	bool hw_check = intel_state->modeset;
>>  	u64 put_domains[I915_MAX_PIPES] = {};
>> -	unsigned crtc_vblank_mask = 0;
>>  	int i;
>>  
>>  	intel_atomic_commit_fence_wait(intel_state);
>> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
>>  
>>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
>> +	dev_priv->display.update_crtcs(state);
>>  
>>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>>  	 * already, but still need the state for the delayed optimization. To
>> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>  	 *   we don't need out special handling any more.
>>  	 */
>>  	if (!state->legacy_cursor_update)
> I think this if is dead code, since either we use the fastpath in
> intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
> then we clear this hack (on most platforms at least) since it just wreaks
> complete havoc.
>
> We should probably do this logic in the async helpers, i.e. if async
> helpers reject a cursor update, clear state->legacy_cursor_update.
>
> Since I think we don't necessarily need this patch in this series, I think
> it'd be better in a separate series to straighten out the async_update
> story. Which would then include moving i915 over to the core support.
Erm, not sure what you mean here, isn't state->legacy_cursor_update mostly false, which means in most cases we'll wait?

~Maarten
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2.
  2017-08-30 12:17 ` [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2 Maarten Lankhorst
@ 2017-08-30 13:09   ` Laurent Pinchart
  2017-08-30 13:34     ` Maarten Lankhorst
  0 siblings, 1 reply; 26+ messages in thread
From: Laurent Pinchart @ 2017-08-30 13:09 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

Hi Maarten,

Thank you for the patch.

On Wednesday, 30 August 2017 15:17:50 EEST Maarten Lankhorst wrote:
> Most code only cares about the current commit or previous commit.
> Fortuantely we already have a place to track those. Move it to
> drm_crtc_state where it belongs. :)
> 
> The per-crtc commit_list is kept for places where we have to look
> deeper than the current or previous commit for checking whether to stall
> on unpin. This is used in drm_atomic_helper_setup_commit and
> intel_has_pending_fb_unpin.
> 
> Changes since v1:
> - Update kerneldoc for drm_crtc.commit_list. (danvet)
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/drm_atomic.c        |  7 ---
>  drivers/gpu/drm/drm_atomic_helper.c | 92 +++++++++-------------------------
>  include/drm/drm_atomic.h            |  1 -
>  include/drm/drm_crtc.h              | 23 ++++++++--
>  4 files changed, 42 insertions(+), 81 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 2fd383d7253a..2cce48f203e0 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct
> drm_atomic_state *state) crtc->funcs->atomic_destroy_state(crtc,
>  						  state->crtcs[i].state);
> 
> -		if (state->crtcs[i].commit) {
> -			kfree(state->crtcs[i].commit->event);
> -			state->crtcs[i].commit->event = NULL;
> -			drm_crtc_commit_put(state->crtcs[i].commit);
> -		}
> -
> -		state->crtcs[i].commit = NULL;
>  		state->crtcs[i].ptr = NULL;
>  		state->crtcs[i].state = NULL;
>  	}
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c index 11d0e94a2181..8ccb8b6536c0
> 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
>  void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
>  					  struct drm_atomic_state *old_state)
>  {
> -	struct drm_crtc_state *unused;
> +	struct drm_crtc_state *new_crtc_state;
>  	struct drm_crtc *crtc;
>  	int i;
> 
> -	for_each_new_crtc_in_state(old_state, crtc, unused, i) {
> -		struct drm_crtc_commit *commit = old_state->crtcs[i].commit;
> +	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
> +		struct drm_crtc_commit *commit = new_crtc_state->commit;
>  		int ret;
> 
>  		if (!commit)
> @@ -1388,11 +1388,10 @@ int drm_atomic_helper_async_check(struct drm_device
> *dev, {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *crtc_state;
> -	struct drm_crtc_commit *commit;
>  	struct drm_plane *__plane, *plane = NULL;
>  	struct drm_plane_state *__plane_state, *plane_state = NULL;
>  	const struct drm_plane_helper_funcs *funcs;
> -	int i, j, n_planes = 0;
> +	int i, n_planes = 0;
> 
>  	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
>  		if (drm_atomic_crtc_needs_modeset(crtc_state))
> @@ -1420,33 +1419,10 @@ int drm_atomic_helper_async_check(struct drm_device
> *dev, return -EINVAL;
> 
>  	/*
> -	 * Don't do an async update if there is an outstanding commit modifying
> +	 * TODO: Don't do an async update if there is an outstanding commit
> modifying
> 	 * the plane.  This prevents our async update's changes from getting
> 	 * overridden by a previous synchronous update's state.
>  	 */

As mentioned in a comment to your previous patch, this is unrelated to 
$SUBJECT. You should mention and explain this change in the commit message 
(possibly splitting it out in a separate commit if you think that would make 
more sense, up to you).

> -	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> -		if (plane->crtc != crtc)
> -			continue;
> -
> -		spin_lock(&crtc->commit_lock);
> -		commit = list_first_entry_or_null(&crtc->commit_list,
> -						  struct drm_crtc_commit,
> -						  commit_entry);
> -		if (!commit) {
> -			spin_unlock(&crtc->commit_lock);
> -			continue;
> -		}
> -		spin_unlock(&crtc->commit_lock);
> -
> -		if (!crtc->state->state)
> -			continue;
> -
> -		for_each_plane_in_state(crtc->state->state, __plane,
> -					__plane_state, j) {
> -			if (__plane == plane)
> -				return -EINVAL;
> -		}
> -	}
> 
>  	return funcs->atomic_async_check(plane, plane_state);
>  }
> @@ -1731,7 +1707,7 @@ int drm_atomic_helper_setup_commit(struct
> drm_atomic_state *state, kref_init(&commit->ref);
>  		commit->crtc = crtc;
> 
> -		state->crtcs[i].commit = commit;
> +		new_crtc_state->commit = commit;
> 
>  		ret = stall_checks(crtc, nonblock);
>  		if (ret)
> @@ -1769,22 +1745,6 @@ int drm_atomic_helper_setup_commit(struct
> drm_atomic_state *state, }
>  EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
> 
> -
> -static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
> -{
> -	struct drm_crtc_commit *commit;
> -	int i = 0;
> -
> -	list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
> -		/* skip the first entry, that's the current commit */
> -		if (i == 1)
> -			return commit;
> -		i++;
> -	}
> -
> -	return NULL;
> -}
> -
>  /**
>   * drm_atomic_helper_wait_for_dependencies - wait for required preceeding
> commits * @old_state: atomic state object with old state structures
> @@ -1800,17 +1760,13 @@ static struct drm_crtc_commit
> *preceeding_commit(struct drm_crtc *crtc) void
> drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
> {
>  	struct drm_crtc *crtc;
> -	struct drm_crtc_state *new_crtc_state;
> +	struct drm_crtc_state *old_crtc_state;
>  	struct drm_crtc_commit *commit;
>  	int i;
>  	long ret;
> 
> -	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
> -		spin_lock(&crtc->commit_lock);
> -		commit = preceeding_commit(crtc);
> -		if (commit)
> -			drm_crtc_commit_get(commit);
> -		spin_unlock(&crtc->commit_lock);
> +	for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
> +		commit = old_crtc_state->commit;
> 
>  		if (!commit)
>  			continue;
> @@ -1828,8 +1784,6 @@ void drm_atomic_helper_wait_for_dependencies(struct
> drm_atomic_state *old_state) if (ret == 0)
>  			DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
>  				  crtc->base.id, crtc->name);
> -
> -		drm_crtc_commit_put(commit);
>  	}
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
> @@ -1857,7 +1811,7 @@ void drm_atomic_helper_commit_hw_done(struct
> drm_atomic_state *old_state) int i;
> 
>  	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
> -		commit = old_state->crtcs[i].commit;
> +		commit = new_crtc_state->commit;
>  		if (!commit)
>  			continue;
> 
> @@ -1887,7 +1841,7 @@ void drm_atomic_helper_commit_cleanup_done(struct
> drm_atomic_state *old_state) int i;
> 
>  	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
> -		commit = old_state->crtcs[i].commit;
> +		commit = new_crtc_state->commit;
>  		if (WARN_ON(!commit))
>  			continue;
> 
> @@ -2277,20 +2231,13 @@ int drm_atomic_helper_swap_state(struct
> drm_atomic_state *state, struct drm_private_state *old_obj_state,
> *new_obj_state;
> 
>  	if (stall) {
> -		for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
> -			spin_lock(&crtc->commit_lock);
> -			commit = list_first_entry_or_null(&crtc->commit_list,
> -					struct drm_crtc_commit, commit_entry);
> -			if (commit)
> -				drm_crtc_commit_get(commit);
> -			spin_unlock(&crtc->commit_lock);
> +		for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
> +			commit = old_crtc_state->commit;
> 
>  			if (!commit)
>  				continue;
> 
>  			ret = wait_for_completion_interruptible(&commit->hw_done);
> -			drm_crtc_commit_put(commit);
> -
>  			if (ret)
>  				return ret;
>  		}
> @@ -2315,13 +2262,13 @@ int drm_atomic_helper_swap_state(struct
> drm_atomic_state *state, state->crtcs[i].state = old_crtc_state;
>  		crtc->state = new_crtc_state;
> 
> -		if (state->crtcs[i].commit) {
> +		if (new_crtc_state->commit) {
>  			spin_lock(&crtc->commit_lock);
> -			list_add(&state->crtcs[i].commit->commit_entry,
> +			list_add(&new_crtc_state->commit->commit_entry,
>  				 &crtc->commit_list);
>  			spin_unlock(&crtc->commit_lock);
> 
> -			state->crtcs[i].commit->event = NULL;
> +			new_crtc_state->commit->event = NULL;
>  		}
>  	}
> 
> @@ -3169,6 +3116,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct
> drm_crtc *crtc, state->connectors_changed = false;
>  	state->color_mgmt_changed = false;
>  	state->zpos_changed = false;
> +	state->commit = NULL;
>  	state->event = NULL;
>  	state->pageflip_flags = 0;
>  }
> @@ -3207,6 +3155,12 @@
> EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); */
>  void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
>  {
> +	if (state->commit) {
> +		kfree(state->commit->event);
> +		state->commit->event = NULL;
> +		drm_crtc_commit_put(state->commit);
> +	}
> +
>  	drm_property_blob_put(state->mode_blob);
>  	drm_property_blob_put(state->degamma_lut);
>  	drm_property_blob_put(state->ctm);
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index 8a5808eb5628..e76d9f95c191 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -144,7 +144,6 @@ struct __drm_planes_state {
>  struct __drm_crtcs_state {
>  	struct drm_crtc *ptr;
>  	struct drm_crtc_state *state, *old_state, *new_state;
> -	struct drm_crtc_commit *commit;
>  	s32 __user *out_fence_ptr;
>  	unsigned last_vblank_count;
>  };
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 1a642020e306..1a01ff4ea023 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -253,6 +253,15 @@ struct drm_crtc_state {
>  	 */
>  	struct drm_pending_vblank_event *event;
> 
> +	/**
> +	 * @commit:
> +	 *
> +	 * This tracks how the commit for this update proceeds through the
> +	 * various phases. This is never cleared, except when we destroy the
> +	 * state, so that subsequent commits can synchronize with previous ones.
> +	 */
> +	struct drm_crtc_commit *commit;
> +
>  	struct drm_atomic_state *state;
>  };
> 
> @@ -808,10 +817,16 @@ struct drm_crtc {
>  	 * @commit_list:
>  	 *
>  	 * List of &drm_crtc_commit structures tracking pending commits.
> -	 * Protected by @commit_lock. This list doesn't hold its own full
> -	 * reference, but burrows it from the ongoing commit. Commit entries
> -	 * must be removed from this list once the commit is fully completed,
> -	 * but before it's correspoding &drm_atomic_state gets destroyed.
> +	 * Protected by @commit_lock. This list holds its own full reference,
> +	 * as does the ongoing commit.
> +	 *
> +	 * "Note that the commit for a state change is also tracked in
> +	 * &drm_crtc_state.commit. For accessing the immediately preceeding
> +	 * commit in an atomic update it is recommended to just use that
> +	 * pointer in the old CRTC state, since accessing that doesn't need
> +	 * any locking or list-walking. @commit_list should only be used to
> +	 * stall for framebuffer cleanup that's signalled through
> +	 * &drm_crtc_commit.cleanup_done."
>  	 */
>  	struct list_head commit_list;


-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2.
  2017-08-30 13:09   ` Laurent Pinchart
@ 2017-08-30 13:34     ` Maarten Lankhorst
  0 siblings, 0 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-30 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, dri-devel; +Cc: intel-gfx

Op 30-08-17 om 15:09 schreef Laurent Pinchart:
> Hi Maarten,
>
> Thank you for the patch.
>
> On Wednesday, 30 August 2017 15:17:50 EEST Maarten Lankhorst wrote:
>> Most code only cares about the current commit or previous commit.
>> Fortuantely we already have a place to track those. Move it to
>> drm_crtc_state where it belongs. :)
>>
>> The per-crtc commit_list is kept for places where we have to look
>> deeper than the current or previous commit for checking whether to stall
>> on unpin. This is used in drm_atomic_helper_setup_commit and
>> intel_has_pending_fb_unpin.
>>
>> Changes since v1:
>> - Update kerneldoc for drm_crtc.commit_list. (danvet)
>>
>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
>> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>> ---
>>  drivers/gpu/drm/drm_atomic.c        |  7 ---
>>  drivers/gpu/drm/drm_atomic_helper.c | 92 +++++++++-------------------------
>>  include/drm/drm_atomic.h            |  1 -
>>  include/drm/drm_crtc.h              | 23 ++++++++--
>>  4 files changed, 42 insertions(+), 81 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
>> index 2fd383d7253a..2cce48f203e0 100644
>> --- a/drivers/gpu/drm/drm_atomic.c
>> +++ b/drivers/gpu/drm/drm_atomic.c
>> @@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct
>> drm_atomic_state *state) crtc->funcs->atomic_destroy_state(crtc,
>>  						  state->crtcs[i].state);
>>
>> -		if (state->crtcs[i].commit) {
>> -			kfree(state->crtcs[i].commit->event);
>> -			state->crtcs[i].commit->event = NULL;
>> -			drm_crtc_commit_put(state->crtcs[i].commit);
>> -		}
>> -
>> -		state->crtcs[i].commit = NULL;
>>  		state->crtcs[i].ptr = NULL;
>>  		state->crtcs[i].state = NULL;
>>  	}
>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
>> b/drivers/gpu/drm/drm_atomic_helper.c index 11d0e94a2181..8ccb8b6536c0
>> 100644
>> --- a/drivers/gpu/drm/drm_atomic_helper.c
>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
>> @@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
>>  void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
>>  					  struct drm_atomic_state *old_state)
>>  {
>> -	struct drm_crtc_state *unused;
>> +	struct drm_crtc_state *new_crtc_state;
>>  	struct drm_crtc *crtc;
>>  	int i;
>>
>> -	for_each_new_crtc_in_state(old_state, crtc, unused, i) {
>> -		struct drm_crtc_commit *commit = old_state->crtcs[i].commit;
>> +	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
>> +		struct drm_crtc_commit *commit = new_crtc_state->commit;
>>  		int ret;
>>
>>  		if (!commit)
>> @@ -1388,11 +1388,10 @@ int drm_atomic_helper_async_check(struct drm_device
>> *dev, {
>>  	struct drm_crtc *crtc;
>>  	struct drm_crtc_state *crtc_state;
>> -	struct drm_crtc_commit *commit;
>>  	struct drm_plane *__plane, *plane = NULL;
>>  	struct drm_plane_state *__plane_state, *plane_state = NULL;
>>  	const struct drm_plane_helper_funcs *funcs;
>> -	int i, j, n_planes = 0;
>> +	int i, n_planes = 0;
>>
>>  	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
>>  		if (drm_atomic_crtc_needs_modeset(crtc_state))
>> @@ -1420,33 +1419,10 @@ int drm_atomic_helper_async_check(struct drm_device
>> *dev, return -EINVAL;
>>
>>  	/*
>> -	 * Don't do an async update if there is an outstanding commit modifying
>> +	 * TODO: Don't do an async update if there is an outstanding commit
>> modifying
>> 	 * the plane.  This prevents our async update's changes from getting
>> 	 * overridden by a previous synchronous update's state.
>>  	 */
> As mentioned in a comment to your previous patch, this is unrelated to 
> $SUBJECT. You should mention and explain this change in the commit message 
> (possibly splitting it out in a separate commit if you think that would make 
> more sense, up to you).
I'm all for removing this, it doesn't work.. But I g uess it can be kept in for now and I'll drop this hunk.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 12:54     ` Maarten Lankhorst
@ 2017-08-30 13:40       ` Daniel Vetter
  2017-08-31 15:18         ` Maarten Lankhorst
  0 siblings, 1 reply; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 13:40 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 02:54:28PM +0200, Maarten Lankhorst wrote:
> Op 30-08-17 om 14:43 schreef Daniel Vetter:
> > On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
> >> The next commit removes the wait for flip_done in in
> >> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
> >> to pass. Instead of using complicated vblank tracking which ends
> >> up being ignored anyway, call the correct atomic helper. :)
> >>
> >> RFC because I'm not completely sure what we want to do with the vblank waiting,
> >> I think for now this patch is the right way to go until we decide because it
> >> preserves the status quo when drm_crtc_commit was introduced.
> >>
> >> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> >> ---
> >>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
> >>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
> >>  2 files changed, 8 insertions(+), 78 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> >> index cbbafbfb0a55..de19621864a9 100644
> >> --- a/drivers/gpu/drm/i915/i915_drv.h
> >> +++ b/drivers/gpu/drm/i915/i915_drv.h
> >> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
> >>  			    struct drm_atomic_state *old_state);
> >>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
> >>  			     struct drm_atomic_state *old_state);
> >> -	void (*update_crtcs)(struct drm_atomic_state *state,
> >> -			     unsigned int *crtc_vblank_mask);
> >> +	void (*update_crtcs)(struct drm_atomic_state *state);
> >>  	void (*audio_codec_enable)(struct drm_connector *connector,
> >>  				   struct intel_encoder *encoder,
> >>  				   const struct drm_display_mode *adjusted_mode);
> >> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> >> index 52c73b4dabaa..3f3cb96aa11e 100644
> >> --- a/drivers/gpu/drm/i915/intel_display.c
> >> +++ b/drivers/gpu/drm/i915/intel_display.c
> >> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
> >>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
> >>  }
> >>  
> >> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
> >> -					  struct drm_i915_private *dev_priv,
> >> -					  unsigned crtc_mask)
> >> -{
> >> -	unsigned last_vblank_count[I915_MAX_PIPES];
> >> -	enum pipe pipe;
> >> -	int ret;
> >> -
> >> -	if (!crtc_mask)
> >> -		return;
> >> -
> >> -	for_each_pipe(dev_priv, pipe) {
> >> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> >> -								  pipe);
> >> -
> >> -		if (!((1 << pipe) & crtc_mask))
> >> -			continue;
> >> -
> >> -		ret = drm_crtc_vblank_get(&crtc->base);
> >> -		if (WARN_ON(ret != 0)) {
> >> -			crtc_mask &= ~(1 << pipe);
> >> -			continue;
> >> -		}
> >> -
> >> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
> >> -	}
> >> -
> >> -	for_each_pipe(dev_priv, pipe) {
> >> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> >> -								  pipe);
> >> -		long lret;
> >> -
> >> -		if (!((1 << pipe) & crtc_mask))
> >> -			continue;
> >> -
> >> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
> >> -				last_vblank_count[pipe] !=
> >> -					drm_crtc_vblank_count(&crtc->base),
> >> -				msecs_to_jiffies(50));
> >> -
> >> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
> >> -
> >> -		drm_crtc_vblank_put(&crtc->base);
> >> -	}
> >> -}
> >> -
> >> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
> >> -{
> >> -	/* fb updated, need to unpin old fb */
> >> -	if (crtc_state->fb_changed)
> >> -		return true;
> >> -
> >> -	/* wm changes, need vblank before final wm's */
> >> -	if (crtc_state->update_wm_post)
> >> -		return true;
> >> -
> >> -	if (crtc_state->wm.need_postvbl_update)
> >> -		return true;
> >> -
> >> -	return false;
> >> -}
> >> -
> >>  static void intel_update_crtc(struct drm_crtc *crtc,
> >>  			      struct drm_atomic_state *state,
> >>  			      struct drm_crtc_state *old_crtc_state,
> >> -			      struct drm_crtc_state *new_crtc_state,
> >> -			      unsigned int *crtc_vblank_mask)
> >> +			      struct drm_crtc_state *new_crtc_state)
> >>  {
> >>  	struct drm_device *dev = crtc->dev;
> >>  	struct drm_i915_private *dev_priv = to_i915(dev);
> >> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
> >>  	}
> >>  
> >>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
> >> -
> >> -	if (needs_vblank_wait(pipe_config))
> >> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
> >>  }
> >>  
> >> -static void intel_update_crtcs(struct drm_atomic_state *state,
> >> -			       unsigned int *crtc_vblank_mask)
> >> +static void intel_update_crtcs(struct drm_atomic_state *state)
> >>  {
> >>  	struct drm_crtc *crtc;
> >>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> >> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
> >>  			continue;
> >>  
> >>  		intel_update_crtc(crtc, state, old_crtc_state,
> >> -				  new_crtc_state, crtc_vblank_mask);
> >> +				  new_crtc_state);
> >>  	}
> >>  }
> >>  
> >> -static void skl_update_crtcs(struct drm_atomic_state *state,
> >> -			     unsigned int *crtc_vblank_mask)
> >> +static void skl_update_crtcs(struct drm_atomic_state *state)
> >>  {
> >>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
> >>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
> >> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
> >>  				vbl_wait = true;
> >>  
> >>  			intel_update_crtc(crtc, state, old_crtc_state,
> >> -					  new_crtc_state, crtc_vblank_mask);
> >> +					  new_crtc_state);
> >>  
> >>  			if (vbl_wait)
> >>  				intel_wait_for_vblank(dev_priv, pipe);
> >> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>  	struct intel_crtc_state *intel_cstate;
> >>  	bool hw_check = intel_state->modeset;
> >>  	u64 put_domains[I915_MAX_PIPES] = {};
> >> -	unsigned crtc_vblank_mask = 0;
> >>  	int i;
> >>  
> >>  	intel_atomic_commit_fence_wait(intel_state);
> >> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
> >>  
> >>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
> >> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
> >> +	dev_priv->display.update_crtcs(state);
> >>  
> >>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
> >>  	 * already, but still need the state for the delayed optimization. To
> >> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>  	 *   we don't need out special handling any more.
> >>  	 */
> >>  	if (!state->legacy_cursor_update)
> > I think this if is dead code, since either we use the fastpath in
> > intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
> > then we clear this hack (on most platforms at least) since it just wreaks
> > complete havoc.
> >
> > We should probably do this logic in the async helpers, i.e. if async
> > helpers reject a cursor update, clear state->legacy_cursor_update.
> >
> > Since I think we don't necessarily need this patch in this series, I think
> > it'd be better in a separate series to straighten out the async_update
> > story. Which would then include moving i915 over to the core support.
> Erm, not sure what you mean here, isn't state->legacy_cursor_update mostly false, which means in most cases we'll wait?

Yup. And the few cases where it's true should be handled by the
fastpath/async plane update logic. Would allow us to ditch a few
conditions all over the code.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-08-30 12:53     ` Maarten Lankhorst
@ 2017-08-30 13:43       ` Daniel Vetter
  0 siblings, 0 replies; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 13:43 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: Gustavo Padovan, intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 02:53:43PM +0200, Maarten Lankhorst wrote:
> Op 30-08-17 om 14:46 schreef Daniel Vetter:
> > On Wed, Aug 30, 2017 at 02:17:52PM +0200, Maarten Lankhorst wrote:
> >> By always keeping track of the last commit in plane_state, we know
> >> whether there is an active update on the plane or not. With that
> >> information we can reject the fast update, and force the slowpath
> >> to be used as was originally intended.
> >>
> >> Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
> >> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > Makes sense, but I think like patch 1 it would be better to do this in a
> > separate series. Which would then include a patch to move i915 over to the
> > async plane support.
> >
> > One more comment below.
> >
> >> ---
> >>  drivers/gpu/drm/drm_atomic_helper.c  | 25 +++++++++++--------------
> >>  drivers/gpu/drm/i915/intel_display.c |  8 ++++++++
> >>  include/drm/drm_plane.h              |  5 +++--
> >>  3 files changed, 22 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> >> index 034f563fb130..384d99347bb3 100644
> >> --- a/drivers/gpu/drm/drm_atomic_helper.c
> >> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> >> @@ -1388,8 +1388,8 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
> >>  {
> >>  	struct drm_crtc *crtc;
> >>  	struct drm_crtc_state *crtc_state;
> >> -	struct drm_plane *__plane, *plane = NULL;
> >> -	struct drm_plane_state *__plane_state, *plane_state = NULL;
> >> +	struct drm_plane *plane;
> >> +	struct drm_plane_state *old_plane_state, *new_plane_state;
> >>  	const struct drm_plane_helper_funcs *funcs;
> >>  	int i, n_planes = 0;
> >>  
> >> @@ -1398,33 +1398,33 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
> >>  			return -EINVAL;
> >>  	}
> >>  
> >> -	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> >> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
> >>  		n_planes++;
> >> -		plane = __plane;
> >> -		plane_state = __plane_state;
> >> -	}
> >>  
> >>  	/* FIXME: we support only single plane updates for now */
> >> -	if (!plane || n_planes != 1)
> >> +	if (n_planes != 1)
> >>  		return -EINVAL;
> >>  
> >> -	if (!plane_state->crtc)
> >> +	if (!new_plane_state->crtc)
> >>  		return -EINVAL;
> >>  
> >>  	funcs = plane->helper_private;
> >>  	if (!funcs->atomic_async_update)
> >>  		return -EINVAL;
> >>  
> >> -	if (plane_state->fence)
> >> +	if (new_plane_state->fence)
> >>  		return -EINVAL;
> >>  
> >>  	/*
> >> -	 * TODO: Don't do an async update if there is an outstanding commit modifying
> >> +	 * Don't do an async update if there is an outstanding commit modifying
> >>  	 * the plane.  This prevents our async update's changes from getting
> >>  	 * overridden by a previous synchronous update's state.
> >>  	 */
> >> +	if (old_plane_state->commit &&
> >> +	    !try_wait_for_completion(&old_plane_state->commit->hw_done))
> >> +		return -EBUSY;
> > Instead of forcing us to always set the plane_state->commit pointer (bunch
> > of pointles refcounting), perhaps just check
> > plane_state->crtc->state->commit? We do hold the necessary locks to at
> > least look at that.
> Then we'd always take the slowpath?
> 
> The point here was to check whether the current plane was part of the
> most recent commit, to know this we must either add a flip_planes mask
> member to drm_crtc_commit, or add a pointer in plane_state to the most
> recent commit it was part of.

Hm right, that would block us against ongoing page_flips ... I guess
tracking commits at the plane level makes sense. If you can add that to
the commit message please, since otherwise I'll forget again?
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 12:17 ` [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2 Maarten Lankhorst
  2017-08-30 12:40   ` Daniel Vetter
@ 2017-08-30 14:10   ` Laurent Pinchart
  2017-08-30 14:17     ` Daniel Vetter
  1 sibling, 1 reply; 26+ messages in thread
From: Laurent Pinchart @ 2017-08-30 14:10 UTC (permalink / raw)
  To: dri-devel; +Cc: intel-gfx

Hi Maarten,

Thank you for the patch.

On Wednesday, 30 August 2017 15:17:51 EEST Maarten Lankhorst wrote:
> Currently we neatly track the crtc state, but forget to look at
> plane/connector state.
> 
> When doing a nonblocking modeset, immediately followed by a setprop
> before the modeset completes, the setprop will see the modesets new
> state as the old state and free it.
> 
> This has to be solved by waiting for hw_done on the connector, even
> if it's not assigned to a crtc. When a connector is unbound we take
> the last crtc commit, and when it stays unbound we create a new
> fake crtc commit for that gets signaled on hw_done for all the
> planes/connectors.
> 
> We wait for it the same way as we do for crtc's, which will make
> sure we never run into a use-after-free situation.
> 
> Changes since v1:
> - Only create a single disable commit. (danvet)
> - Fix leak in intel_legacy_cursor_update.
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/gpu/drm/drm_atomic.c         |   4 +
>  drivers/gpu/drm/drm_atomic_helper.c  | 156 ++++++++++++++++++++++++++++++--
>  drivers/gpu/drm/i915/intel_display.c |   2 +
>  include/drm/drm_atomic.h             |  12 +++
>  include/drm/drm_connector.h          |   7 ++
>  include/drm/drm_plane.h              |   7 ++
>  6 files changed, 182 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 2cce48f203e0..75f5f74de9bf 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct
> drm_atomic_state *state) }
>  	state->num_private_objs = 0;
> 
> +	if (state->fake_commit) {
> +		drm_crtc_commit_put(state->fake_commit);
> +		state->fake_commit = NULL;
> +	}
>  }
>  EXPORT_SYMBOL(drm_atomic_state_default_clear);
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c index 8ccb8b6536c0..034f563fb130
> 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion
> *completion) drm_crtc_commit_put(commit);
>  }
> 
> +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc
> *crtc)
> +{

You could allocate the commit in this function too, the kzalloc() is currently 
duplicated. The function should probably be called alloc_commit() then.

> +	init_completion(&commit->flip_done);
> +	init_completion(&commit->hw_done);
> +	init_completion(&commit->cleanup_done);
> +	INIT_LIST_HEAD(&commit->commit_entry);
> +	kref_init(&commit->ref);
> +	commit->crtc = crtc;
> +}
> +
> +static struct drm_crtc_commit *
> +fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
> +{
> +	struct drm_crtc_commit *commit;
> +
> +	if (crtc) {
> +		struct drm_crtc_state *new_crtc_state;
> +
> +		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> +		commit = new_crtc_state->commit;
> +	} else if (!state->fake_commit) {
> +		state->fake_commit = commit = kzalloc(sizeof(*commit), GFP_KERNEL);
> +		if (!commit)
> +			return NULL;
> +
> +		init_commit(commit, NULL);
> +	} else
> +		commit = state->fake_commit;
> +
> +	drm_crtc_commit_get(commit);

I believe the reference counting is right. The double reference in the second 
case (kref_init() when initializing the commit and drm_crtc_commit_get()) 
should not cause a leak. The kref_init() takes a reference to store the commit 
in state->fake_commit, released in drm_atomic_state_default_clear(), and the 
drm_crtc_commit_get() takes a reference returned by the function, stored in 
new_*_state->commit by the caller.

This being said, I think the reference counting is confusing, as proved by 
Daniel thinking there was a leak here (or by me thinking there's no leak while 
there's one :-)). To make the implementation clearer, I propose turning the 
definition of drm_crtc_commit_get() to

static inline struct drm_crtc_commit *
drm_crtc_commit_get(struct drm_crtc_commit *commit)
{
	kref_get(&commit->ref);
	return commit;
}

and writing this function as

/* Return a new reference to the commit object */
static struct drm_crtc_commit *
fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
{
	struct drm_crtc_commit *commit;

	if (crtc) {
		struct drm_crtc_state *new_crtc_state;

		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);

		commit = new_crtc_state->commit;
	} else {
		if (!state->fake_commit)
			state->fake_commit = alloc_commit(NULL);

		commit = state->fake_commit;
	}

	return drm_crtc_commit_get(commit);
}

> +	return commit;
> +}
> +
>  /**
>   * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
>   * @state: new modeset state to be committed
> @@ -1692,6 +1726,10 @@ int drm_atomic_helper_setup_commit(struct
> drm_atomic_state *state, {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> +	struct drm_connector *conn;
> +	struct drm_connector_state *old_conn_state, *new_conn_state;
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state, *new_plane_state;
>  	struct drm_crtc_commit *commit;
>  	int i, ret;
> 
> @@ -1700,12 +1738,7 @@ int drm_atomic_helper_setup_commit(struct
> drm_atomic_state *state, if (!commit)
>  			return -ENOMEM;
> 
> -		init_completion(&commit->flip_done);
> -		init_completion(&commit->hw_done);
> -		init_completion(&commit->cleanup_done);
> -		INIT_LIST_HEAD(&commit->commit_entry);
> -		kref_init(&commit->ref);
> -		commit->crtc = crtc;
> +		init_commit(commit, crtc);
> 
>  		new_crtc_state->commit = commit;
> 
> @@ -1741,6 +1774,36 @@ int drm_atomic_helper_setup_commit(struct
> drm_atomic_state *state, drm_crtc_commit_get(commit);
>  	}
> 
> +	for_each_oldnew_connector_in_state(state, conn, old_conn_state,
> new_conn_state, i) {
> +		if (new_conn_state->crtc)
> +			continue;
> +
> +		if (nonblock && old_conn_state->commit &&
> +		    !try_wait_for_completion(&old_conn_state->commit->flip_done))
> +			return -EBUSY;

flip_done only, not hw_done ? A comment that explains why would be helpful.

> +
> +		commit = fake_or_crtc_commit(state, old_conn_state->crtc);
> +		if (!commit)
> +			return -ENOMEM;
> +
> +		new_conn_state->commit = commit;
> +	}
> +
> +	for_each_oldnew_plane_in_state(state, plane, old_plane_state,
> new_plane_state, i) {
> +		if (new_plane_state->crtc)
> +			continue;
> +
> +		if (nonblock && old_plane_state->commit &&
> +		    !try_wait_for_completion(&old_plane_state->commit->flip_done))
> +			return -EBUSY;

Same here.

> +		commit = fake_or_crtc_commit(state, old_plane_state->crtc);
> +		if (!commit)
> +			return -ENOMEM;
> +
> +		new_plane_state->commit = commit;
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
> @@ -1761,6 +1824,10 @@ void drm_atomic_helper_wait_for_dependencies(struct
> drm_atomic_state *old_state) {
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state;
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state;
> +	struct drm_connector *conn;
> +	struct drm_connector_state *old_conn_state;
>  	struct drm_crtc_commit *commit;
>  	int i;
>  	long ret;
> @@ -1785,6 +1852,48 @@ void drm_atomic_helper_wait_for_dependencies(struct
> drm_atomic_state *old_state) DRM_ERROR("[CRTC:%d:%s] flip_done timed
> out\n",
>  				  crtc->base.id, crtc->name);
>  	}
> +
> +	for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
> +		commit = old_conn_state->commit;
> +
> +		if (!commit)
> +			continue;
> +
> +		ret = wait_for_completion_timeout(&commit->hw_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
> +				  conn->base.id, conn->name);
> +
> +		/* Currently no support for overwriting flips, hence
> +		 * stall for previous one to execute completely. */
> +		ret = wait_for_completion_timeout(&commit->flip_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
> +				  conn->base.id, conn->name);

There's probably something I don't get, but given that the connector state -
>commit pointer is only set for commits that have no CRTC, do we have a 
guarantee that there will be a page flip to be signalled ? Can't we for 
instance disable a connector without a page flip ? Or set a property on an 
already disabled connector ?

> +	}
> +
> +	for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
> +		commit = old_plane_state->commit;
> +
> +		if (!commit)
> +			continue;
> +
> +		ret = wait_for_completion_timeout(&commit->hw_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
> +				  plane->base.id, plane->name);
> +
> +		/* Currently no support for overwriting flips, hence
> +		 * stall for previous one to execute completely. */
> +		ret = wait_for_completion_timeout(&commit->flip_done,
> +						  10*HZ);
> +		if (ret == 0)
> +			DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
> +				  plane->base.id, plane->name);
> +	}
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
> 
> @@ -1819,6 +1928,11 @@ void drm_atomic_helper_commit_hw_done(struct
> drm_atomic_state *old_state) WARN_ON(new_crtc_state->event);
>  		complete_all(&commit->hw_done);
>  	}
> +
> +	if (old_state->fake_commit) {
> +		complete_all(&old_state->fake_commit->hw_done);
> +		complete_all(&old_state->fake_commit->flip_done);
> +	}
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
> 
> @@ -2241,6 +2355,28 @@ int drm_atomic_helper_swap_state(struct
> drm_atomic_state *state, if (ret)
>  				return ret;
>  		}
> +

A comment would be helpful here too. In particular explaining why you wait on 
hw_done only isn't straightforward.

> +		for_each_old_connector_in_state(state, connector, old_conn_state, i) {
> +			commit = old_conn_state->commit;
> +
> +			if (!commit)
> +				continue;
> +
> +			ret = wait_for_completion_interruptible(&commit->hw_done);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		for_each_old_plane_in_state(state, plane, old_plane_state, i) {
> +			commit = old_plane_state->commit;
> +
> +			if (!commit)
> +				continue;
> +
> +			ret = wait_for_completion_interruptible(&commit->hw_done);
> +			if (ret)
> +				return ret;
> +		}
>  	}
> 
>  	for_each_oldnew_connector_in_state(state, connector, old_conn_state,
> new_conn_state, i) { @@ -3223,6 +3359,7 @@ void
> __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
> drm_framebuffer_get(state->fb);
> 
>  	state->fence = NULL;
> +	state->commit = NULL;
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
> 
> @@ -3264,6 +3401,9 @@ void __drm_atomic_helper_plane_destroy_state(struct
> drm_plane_state *state)
> 
>  	if (state->fence)
>  		dma_fence_put(state->fence);
> +
> +	if (state->commit)
> +		drm_crtc_commit_put(state->commit);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
> 
> @@ -3342,6 +3482,7 @@ __drm_atomic_helper_connector_duplicate_state(struct
> drm_connector *connector, memcpy(state, connector->state, sizeof(*state));
>  	if (state->crtc)
>  		drm_connector_get(connector);
> +	state->commit = NULL;
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
> 
> @@ -3468,6 +3609,9 @@ __drm_atomic_helper_connector_destroy_state(struct
> drm_connector_state *state) {
>  	if (state->crtc)
>  		drm_connector_put(state->connector);
> +
> +	if (state->commit)
> +		drm_crtc_commit_put(state->commit);
>  }
>  EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
> 
> diff --git a/drivers/gpu/drm/i915/intel_display.c
> b/drivers/gpu/drm/i915/intel_display.c index 3f3cb96aa11e..70ce02753177
> 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -13109,8 +13109,10 @@ intel_legacy_cursor_update(struct drm_plane *plane,
> 
>  	/* Swap plane state */
>  	new_plane_state->fence = old_plane_state->fence;
> +	new_plane_state->commit = old_plane_state->commit;
>  	*to_intel_plane_state(old_plane_state) =
> *to_intel_plane_state(new_plane_state); new_plane_state->fence = NULL;
> +	new_plane_state->commit = NULL;
>  	new_plane_state->fb = old_fb;
>  	to_intel_plane_state(new_plane_state)->vma = NULL;
> 
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index e76d9f95c191..ba976f4a4938 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -236,6 +236,18 @@ struct drm_atomic_state {
>  	struct drm_modeset_acquire_ctx *acquire_ctx;
> 
>  	/**
> +	 * @fake_commit:
> +	 *
> +	 * Used for signaling unbound planes/connectors.
> +	 * When a connector or plane is not bound to any CRTC, it's still
> important
> +	 * to preserve linearity to prevent the atomic states from being freed to
> early.
> +	 *
> +	 * This commit (if set) is not bound to any crtc, but will be completed
> when
> +	 * drm_atomic_helper_commit_hw_done() is called.
> +	 */
> +	struct drm_crtc_commit *fake_commit;
> +
> +	/**
>  	 * @commit_work:
>  	 *
>  	 * Work item which can be used by the driver or helpers to execute the
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index ea8da401c93c..8837649d16e8 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -347,6 +347,13 @@ struct drm_connector_state {
> 
>  	struct drm_atomic_state *state;
> 
> +	/**
> +	 * @commit: Tracks the pending commit to prevent use-after-free
> conditions.
> +	 *
> +	 * Is only set when @crtc is NULL.
> +	 */
> +	struct drm_crtc_commit *commit;
> +
>  	struct drm_tv_connector_state tv;
> 
>  	/**
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 73f90f9d057f..7d96116fd4c4 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -123,6 +123,13 @@ struct drm_plane_state {
>  	 */
>  	bool visible;
> 
> +	/**
> +	 * @commit: Tracks the pending commit to prevent use-after-free
> conditions.
> +	 *
> +	 * Is only set when @crtc is NULL.
> +	 */
> +	struct drm_crtc_commit *commit;
> +
>  	struct drm_atomic_state *state;
>  };


-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 14:10   ` Laurent Pinchart
@ 2017-08-30 14:17     ` Daniel Vetter
  2017-08-30 14:44       ` [Intel-gfx] " Laurent Pinchart
  0 siblings, 1 reply; 26+ messages in thread
From: Daniel Vetter @ 2017-08-30 14:17 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: intel-gfx, dri-devel

On Wed, Aug 30, 2017 at 05:10:43PM +0300, Laurent Pinchart wrote:
> Hi Maarten,
> 
> Thank you for the patch.
> 
> On Wednesday, 30 August 2017 15:17:51 EEST Maarten Lankhorst wrote:
> > Currently we neatly track the crtc state, but forget to look at
> > plane/connector state.
> > 
> > When doing a nonblocking modeset, immediately followed by a setprop
> > before the modeset completes, the setprop will see the modesets new
> > state as the old state and free it.
> > 
> > This has to be solved by waiting for hw_done on the connector, even
> > if it's not assigned to a crtc. When a connector is unbound we take
> > the last crtc commit, and when it stays unbound we create a new
> > fake crtc commit for that gets signaled on hw_done for all the
> > planes/connectors.
> > 
> > We wait for it the same way as we do for crtc's, which will make
> > sure we never run into a use-after-free situation.
> > 
> > Changes since v1:
> > - Only create a single disable commit. (danvet)
> > - Fix leak in intel_legacy_cursor_update.
> > 
> > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
> > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/drm_atomic.c         |   4 +
> >  drivers/gpu/drm/drm_atomic_helper.c  | 156 ++++++++++++++++++++++++++++++--
> >  drivers/gpu/drm/i915/intel_display.c |   2 +
> >  include/drm/drm_atomic.h             |  12 +++
> >  include/drm/drm_connector.h          |   7 ++
> >  include/drm/drm_plane.h              |   7 ++
> >  6 files changed, 182 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > index 2cce48f203e0..75f5f74de9bf 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct
> > drm_atomic_state *state) }
> >  	state->num_private_objs = 0;
> > 
> > +	if (state->fake_commit) {
> > +		drm_crtc_commit_put(state->fake_commit);
> > +		state->fake_commit = NULL;
> > +	}
> >  }
> >  EXPORT_SYMBOL(drm_atomic_state_default_clear);
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> > b/drivers/gpu/drm/drm_atomic_helper.c index 8ccb8b6536c0..034f563fb130
> > 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion
> > *completion) drm_crtc_commit_put(commit);
> >  }
> > 
> > +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc
> > *crtc)
> > +{
> 
> You could allocate the commit in this function too, the kzalloc() is currently 
> duplicated. The function should probably be called alloc_commit() then.
> 
> > +	init_completion(&commit->flip_done);
> > +	init_completion(&commit->hw_done);
> > +	init_completion(&commit->cleanup_done);
> > +	INIT_LIST_HEAD(&commit->commit_entry);
> > +	kref_init(&commit->ref);
> > +	commit->crtc = crtc;
> > +}
> > +
> > +static struct drm_crtc_commit *
> > +fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
> > +{
> > +	struct drm_crtc_commit *commit;
> > +
> > +	if (crtc) {
> > +		struct drm_crtc_state *new_crtc_state;
> > +
> > +		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > +
> > +		commit = new_crtc_state->commit;
> > +	} else if (!state->fake_commit) {
> > +		state->fake_commit = commit = kzalloc(sizeof(*commit), GFP_KERNEL);
> > +		if (!commit)
> > +			return NULL;
> > +
> > +		init_commit(commit, NULL);
> > +	} else
> > +		commit = state->fake_commit;
> > +
> > +	drm_crtc_commit_get(commit);
> 
> I believe the reference counting is right. The double reference in the second 
> case (kref_init() when initializing the commit and drm_crtc_commit_get()) 
> should not cause a leak. The kref_init() takes a reference to store the commit 
> in state->fake_commit, released in drm_atomic_state_default_clear(), and the 
> drm_crtc_commit_get() takes a reference returned by the function, stored in 
> new_*_state->commit by the caller.
> 
> This being said, I think the reference counting is confusing, as proved by 
> Daniel thinking there was a leak here (or by me thinking there's no leak while 
> there's one :-)). To make the implementation clearer, I propose turning the 
> definition of drm_crtc_commit_get() to
> 
> static inline struct drm_crtc_commit *
> drm_crtc_commit_get(struct drm_crtc_commit *commit)
> {
> 	kref_get(&commit->ref);
> 	return commit;
> }
> 
> and writing this function as
> 
> /* Return a new reference to the commit object */
> static struct drm_crtc_commit *
> fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
> {
> 	struct drm_crtc_commit *commit;
> 
> 	if (crtc) {
> 		struct drm_crtc_state *new_crtc_state;
> 
> 		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> 
> 		commit = new_crtc_state->commit;
> 	} else {
> 		if (!state->fake_commit)
> 			state->fake_commit = alloc_commit(NULL);
> 
> 		commit = state->fake_commit;
> 	}
> 
> 	return drm_crtc_commit_get(commit);
> }

+1 on something like this, the compressed layout with assigning both
commit and ->fake_commit is what tricked me into seeing a leak.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [Intel-gfx] [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2.
  2017-08-30 14:17     ` Daniel Vetter
@ 2017-08-30 14:44       ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2017-08-30 14:44 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

On Wednesday, 30 August 2017 17:17:36 EEST Daniel Vetter wrote:
> On Wed, Aug 30, 2017 at 05:10:43PM +0300, Laurent Pinchart wrote:
> > Hi Maarten,
> > 
> > Thank you for the patch.
> > 
> > On Wednesday, 30 August 2017 15:17:51 EEST Maarten Lankhorst wrote:
> > > Currently we neatly track the crtc state, but forget to look at
> > > plane/connector state.
> > > 
> > > When doing a nonblocking modeset, immediately followed by a setprop
> > > before the modeset completes, the setprop will see the modesets new
> > > state as the old state and free it.
> > > 
> > > This has to be solved by waiting for hw_done on the connector, even
> > > if it's not assigned to a crtc. When a connector is unbound we take
> > > the last crtc commit, and when it stays unbound we create a new
> > > fake crtc commit for that gets signaled on hw_done for all the
> > > planes/connectors.
> > > 
> > > We wait for it the same way as we do for crtc's, which will make
> > > sure we never run into a use-after-free situation.
> > > 
> > > Changes since v1:
> > > - Only create a single disable commit. (danvet)
> > > - Fix leak in intel_legacy_cursor_update.
> > > 
> > > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > > Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind*
> > > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > > 
> > >  drivers/gpu/drm/drm_atomic.c         |   4 +
> > >  drivers/gpu/drm/drm_atomic_helper.c  | 156
> > >  ++++++++++++++++++++++++++++++--
> > >  drivers/gpu/drm/i915/intel_display.c |   2 +
> > >  include/drm/drm_atomic.h             |  12 +++
> > >  include/drm/drm_connector.h          |   7 ++
> > >  include/drm/drm_plane.h              |   7 ++
> > >  6 files changed, 182 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > > index 2cce48f203e0..75f5f74de9bf 100644
> > > --- a/drivers/gpu/drm/drm_atomic.c
> > > +++ b/drivers/gpu/drm/drm_atomic.c
> > > @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct
> > > drm_atomic_state *state) }
> > > 
> > >  	state->num_private_objs = 0;
> > > 
> > > +	if (state->fake_commit) {
> > > +		drm_crtc_commit_put(state->fake_commit);
> > > +		state->fake_commit = NULL;
> > > +	}
> > > 
> > >  }
> > >  EXPORT_SYMBOL(drm_atomic_state_default_clear);
> > > 
> > > diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> > > b/drivers/gpu/drm/drm_atomic_helper.c index 8ccb8b6536c0..034f563fb130
> > > 100644
> > > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > > @@ -1644,6 +1644,40 @@ static void release_crtc_commit(struct completion
> > > *completion) drm_crtc_commit_put(commit);
> > > 
> > >  }
> > > 
> > > +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc
> > > *crtc)
> > > +{
> > 
> > You could allocate the commit in this function too, the kzalloc() is
> > currently duplicated. The function should probably be called
> > alloc_commit() then.> 
> > > +	init_completion(&commit->flip_done);
> > > +	init_completion(&commit->hw_done);
> > > +	init_completion(&commit->cleanup_done);
> > > +	INIT_LIST_HEAD(&commit->commit_entry);
> > > +	kref_init(&commit->ref);
> > > +	commit->crtc = crtc;
> > > +}
> > > +
> > > +static struct drm_crtc_commit *
> > > +fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc
> > > *crtc)
> > > +{
> > > +	struct drm_crtc_commit *commit;
> > > +
> > > +	if (crtc) {
> > > +		struct drm_crtc_state *new_crtc_state;
> > > +
> > > +		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > > +
> > > +		commit = new_crtc_state->commit;
> > > +	} else if (!state->fake_commit) {
> > > +		state->fake_commit = commit = kzalloc(sizeof(*commit), 
GFP_KERNEL);
> > > +		if (!commit)
> > > +			return NULL;
> > > +
> > > +		init_commit(commit, NULL);
> > > +	} else
> > > +		commit = state->fake_commit;
> > > +
> > > +	drm_crtc_commit_get(commit);
> > 
> > I believe the reference counting is right. The double reference in the
> > second case (kref_init() when initializing the commit and
> > drm_crtc_commit_get()) should not cause a leak. The kref_init() takes a
> > reference to store the commit in state->fake_commit, released in
> > drm_atomic_state_default_clear(), and the drm_crtc_commit_get() takes a
> > reference returned by the function, stored in new_*_state->commit by the
> > caller.
> > 
> > This being said, I think the reference counting is confusing, as proved by
> > Daniel thinking there was a leak here (or by me thinking there's no leak
> > while there's one :-)). To make the implementation clearer, I propose
> > turning the definition of drm_crtc_commit_get() to
> > 
> > static inline struct drm_crtc_commit *
> > drm_crtc_commit_get(struct drm_crtc_commit *commit)
> > {
> > 
> > 	kref_get(&commit->ref);
> > 	return commit;
> > 
> > }
> > 
> > and writing this function as
> > 
> > /* Return a new reference to the commit object */
> > static struct drm_crtc_commit *
> > fake_or_crtc_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
> > {
> > 
> > 	struct drm_crtc_commit *commit;
> > 	
> > 	if (crtc) {
> > 	
> > 		struct drm_crtc_state *new_crtc_state;
> > 		
> > 		new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> > 		
> > 		commit = new_crtc_state->commit;
> > 	
> > 	} else {
> > 	
> > 		if (!state->fake_commit)
> > 		
> > 			state->fake_commit = alloc_commit(NULL);
> > 		
> > 		commit = state->fake_commit;
> > 	
> > 	}
> > 	
> > 	return drm_crtc_commit_get(commit);
> > 
> > }
> 
> +1 on something like this, the compressed layout with assigning both
> commit and ->fake_commit is what tricked me into seeing a leak.

What I think makes it clearer is to store the return value of functions 
returning a reference directly in the pointer that stores the reference. 
That's why I prefer

1.		if (!state->fake_commit)
2.			state->fake_commit = alloc_commit(NULL);
3.
4.		commit = state->fake_commit;

Line 2 stores the pointer in an object that thus requires a reference, which 
is returned by alloc_commit(). Line 4 stores the pointer in a local variable, 
and thus doesn't require a reference.

-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 13:40       ` Daniel Vetter
@ 2017-08-31 15:18         ` Maarten Lankhorst
  2017-09-04  7:30           ` Daniel Vetter
  0 siblings, 1 reply; 26+ messages in thread
From: Maarten Lankhorst @ 2017-08-31 15:18 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

Op 30-08-17 om 15:40 schreef Daniel Vetter:
> On Wed, Aug 30, 2017 at 02:54:28PM +0200, Maarten Lankhorst wrote:
>> Op 30-08-17 om 14:43 schreef Daniel Vetter:
>>> On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
>>>> The next commit removes the wait for flip_done in in
>>>> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
>>>> to pass. Instead of using complicated vblank tracking which ends
>>>> up being ignored anyway, call the correct atomic helper. :)
>>>>
>>>> RFC because I'm not completely sure what we want to do with the vblank waiting,
>>>> I think for now this patch is the right way to go until we decide because it
>>>> preserves the status quo when drm_crtc_commit was introduced.
>>>>
>>>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
>>>> ---
>>>>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
>>>>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
>>>>  2 files changed, 8 insertions(+), 78 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>>>> index cbbafbfb0a55..de19621864a9 100644
>>>> --- a/drivers/gpu/drm/i915/i915_drv.h
>>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>>>> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
>>>>  			    struct drm_atomic_state *old_state);
>>>>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
>>>>  			     struct drm_atomic_state *old_state);
>>>> -	void (*update_crtcs)(struct drm_atomic_state *state,
>>>> -			     unsigned int *crtc_vblank_mask);
>>>> +	void (*update_crtcs)(struct drm_atomic_state *state);
>>>>  	void (*audio_codec_enable)(struct drm_connector *connector,
>>>>  				   struct intel_encoder *encoder,
>>>>  				   const struct drm_display_mode *adjusted_mode);
>>>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>>>> index 52c73b4dabaa..3f3cb96aa11e 100644
>>>> --- a/drivers/gpu/drm/i915/intel_display.c
>>>> +++ b/drivers/gpu/drm/i915/intel_display.c
>>>> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
>>>>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
>>>>  }
>>>>  
>>>> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
>>>> -					  struct drm_i915_private *dev_priv,
>>>> -					  unsigned crtc_mask)
>>>> -{
>>>> -	unsigned last_vblank_count[I915_MAX_PIPES];
>>>> -	enum pipe pipe;
>>>> -	int ret;
>>>> -
>>>> -	if (!crtc_mask)
>>>> -		return;
>>>> -
>>>> -	for_each_pipe(dev_priv, pipe) {
>>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>>>> -								  pipe);
>>>> -
>>>> -		if (!((1 << pipe) & crtc_mask))
>>>> -			continue;
>>>> -
>>>> -		ret = drm_crtc_vblank_get(&crtc->base);
>>>> -		if (WARN_ON(ret != 0)) {
>>>> -			crtc_mask &= ~(1 << pipe);
>>>> -			continue;
>>>> -		}
>>>> -
>>>> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
>>>> -	}
>>>> -
>>>> -	for_each_pipe(dev_priv, pipe) {
>>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>>>> -								  pipe);
>>>> -		long lret;
>>>> -
>>>> -		if (!((1 << pipe) & crtc_mask))
>>>> -			continue;
>>>> -
>>>> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
>>>> -				last_vblank_count[pipe] !=
>>>> -					drm_crtc_vblank_count(&crtc->base),
>>>> -				msecs_to_jiffies(50));
>>>> -
>>>> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
>>>> -
>>>> -		drm_crtc_vblank_put(&crtc->base);
>>>> -	}
>>>> -}
>>>> -
>>>> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
>>>> -{
>>>> -	/* fb updated, need to unpin old fb */
>>>> -	if (crtc_state->fb_changed)
>>>> -		return true;
>>>> -
>>>> -	/* wm changes, need vblank before final wm's */
>>>> -	if (crtc_state->update_wm_post)
>>>> -		return true;
>>>> -
>>>> -	if (crtc_state->wm.need_postvbl_update)
>>>> -		return true;
>>>> -
>>>> -	return false;
>>>> -}
>>>> -
>>>>  static void intel_update_crtc(struct drm_crtc *crtc,
>>>>  			      struct drm_atomic_state *state,
>>>>  			      struct drm_crtc_state *old_crtc_state,
>>>> -			      struct drm_crtc_state *new_crtc_state,
>>>> -			      unsigned int *crtc_vblank_mask)
>>>> +			      struct drm_crtc_state *new_crtc_state)
>>>>  {
>>>>  	struct drm_device *dev = crtc->dev;
>>>>  	struct drm_i915_private *dev_priv = to_i915(dev);
>>>> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
>>>>  	}
>>>>  
>>>>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
>>>> -
>>>> -	if (needs_vblank_wait(pipe_config))
>>>> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
>>>>  }
>>>>  
>>>> -static void intel_update_crtcs(struct drm_atomic_state *state,
>>>> -			       unsigned int *crtc_vblank_mask)
>>>> +static void intel_update_crtcs(struct drm_atomic_state *state)
>>>>  {
>>>>  	struct drm_crtc *crtc;
>>>>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>>>> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
>>>>  			continue;
>>>>  
>>>>  		intel_update_crtc(crtc, state, old_crtc_state,
>>>> -				  new_crtc_state, crtc_vblank_mask);
>>>> +				  new_crtc_state);
>>>>  	}
>>>>  }
>>>>  
>>>> -static void skl_update_crtcs(struct drm_atomic_state *state,
>>>> -			     unsigned int *crtc_vblank_mask)
>>>> +static void skl_update_crtcs(struct drm_atomic_state *state)
>>>>  {
>>>>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
>>>>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
>>>> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
>>>>  				vbl_wait = true;
>>>>  
>>>>  			intel_update_crtc(crtc, state, old_crtc_state,
>>>> -					  new_crtc_state, crtc_vblank_mask);
>>>> +					  new_crtc_state);
>>>>  
>>>>  			if (vbl_wait)
>>>>  				intel_wait_for_vblank(dev_priv, pipe);
>>>> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>  	struct intel_crtc_state *intel_cstate;
>>>>  	bool hw_check = intel_state->modeset;
>>>>  	u64 put_domains[I915_MAX_PIPES] = {};
>>>> -	unsigned crtc_vblank_mask = 0;
>>>>  	int i;
>>>>  
>>>>  	intel_atomic_commit_fence_wait(intel_state);
>>>> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
>>>>  
>>>>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>>>> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
>>>> +	dev_priv->display.update_crtcs(state);
>>>>  
>>>>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>>>>  	 * already, but still need the state for the delayed optimization. To
>>>> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>  	 *   we don't need out special handling any more.
>>>>  	 */
>>>>  	if (!state->legacy_cursor_update)
>>> I think this if is dead code, since either we use the fastpath in
>>> intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
>>> then we clear this hack (on most platforms at least) since it just wreaks
>>> complete havoc.
>>>
>>> We should probably do this logic in the async helpers, i.e. if async
>>> helpers reject a cursor update, clear state->legacy_cursor_update.
>>>
>>> Since I think we don't necessarily need this patch in this series, I think
>>> it'd be better in a separate series to straighten out the async_update
>>> story. Which would then include moving i915 over to the core support.
>> Erm, not sure what you mean here, isn't state->legacy_cursor_update mostly false, which means in most cases we'll wait?
> Yup. And the few cases where it's true should be handled by the
> fastpath/async plane update logic. Would allow us to ditch a few
> conditions all over the code.
> -Daniel

I've done some more tests, and it seems this patch is required.

Without this we may not always wait, and that could result in spurious failures of page flip with -EBUSY.
I'd love to hide this from userspace, but I don't think that's possible yet. It's best to always call
drm_atomic_helper_wait_for_flip_done() for now. It's a noop for legacy_cursor_update anyway.
commit->flip_done is always set early.

~Maarten

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-08-30 12:46   ` Daniel Vetter
  2017-08-30 12:53     ` Maarten Lankhorst
@ 2017-09-02 18:59     ` Gustavo Padovan
  2017-09-04  7:31       ` Daniel Vetter
  1 sibling, 1 reply; 26+ messages in thread
From: Gustavo Padovan @ 2017-09-02 18:59 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

2017-08-30 Daniel Vetter <daniel@ffwll.ch>:

> On Wed, Aug 30, 2017 at 02:17:52PM +0200, Maarten Lankhorst wrote:
> > By always keeping track of the last commit in plane_state, we know
> > whether there is an active update on the plane or not. With that
> > information we can reject the fast update, and force the slowpath
> > to be used as was originally intended.
> > 
> > Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
> > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> 
> Makes sense, but I think like patch 1 it would be better to do this in a
> separate series. Which would then include a patch to move i915 over to the
> async plane support.

This patch makes sense to me and it is better than the fix I wrote but never
got around to send it out. I can pick in here locally, put the patches I
have here for the drivers on top of it and send to intel-gfx for CI.

Anyway, without the i915 change, this is

Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.com>

Gustavo

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-31 15:18         ` Maarten Lankhorst
@ 2017-09-04  7:30           ` Daniel Vetter
  2017-09-04  7:55             ` Maarten Lankhorst
  0 siblings, 1 reply; 26+ messages in thread
From: Daniel Vetter @ 2017-09-04  7:30 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx, dri-devel

On Thu, Aug 31, 2017 at 05:18:06PM +0200, Maarten Lankhorst wrote:
> Op 30-08-17 om 15:40 schreef Daniel Vetter:
> > On Wed, Aug 30, 2017 at 02:54:28PM +0200, Maarten Lankhorst wrote:
> >> Op 30-08-17 om 14:43 schreef Daniel Vetter:
> >>> On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
> >>>> The next commit removes the wait for flip_done in in
> >>>> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
> >>>> to pass. Instead of using complicated vblank tracking which ends
> >>>> up being ignored anyway, call the correct atomic helper. :)
> >>>>
> >>>> RFC because I'm not completely sure what we want to do with the vblank waiting,
> >>>> I think for now this patch is the right way to go until we decide because it
> >>>> preserves the status quo when drm_crtc_commit was introduced.
> >>>>
> >>>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> >>>> ---
> >>>>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
> >>>>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
> >>>>  2 files changed, 8 insertions(+), 78 deletions(-)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> >>>> index cbbafbfb0a55..de19621864a9 100644
> >>>> --- a/drivers/gpu/drm/i915/i915_drv.h
> >>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
> >>>> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
> >>>>  			    struct drm_atomic_state *old_state);
> >>>>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
> >>>>  			     struct drm_atomic_state *old_state);
> >>>> -	void (*update_crtcs)(struct drm_atomic_state *state,
> >>>> -			     unsigned int *crtc_vblank_mask);
> >>>> +	void (*update_crtcs)(struct drm_atomic_state *state);
> >>>>  	void (*audio_codec_enable)(struct drm_connector *connector,
> >>>>  				   struct intel_encoder *encoder,
> >>>>  				   const struct drm_display_mode *adjusted_mode);
> >>>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> >>>> index 52c73b4dabaa..3f3cb96aa11e 100644
> >>>> --- a/drivers/gpu/drm/i915/intel_display.c
> >>>> +++ b/drivers/gpu/drm/i915/intel_display.c
> >>>> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
> >>>>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
> >>>>  }
> >>>>  
> >>>> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
> >>>> -					  struct drm_i915_private *dev_priv,
> >>>> -					  unsigned crtc_mask)
> >>>> -{
> >>>> -	unsigned last_vblank_count[I915_MAX_PIPES];
> >>>> -	enum pipe pipe;
> >>>> -	int ret;
> >>>> -
> >>>> -	if (!crtc_mask)
> >>>> -		return;
> >>>> -
> >>>> -	for_each_pipe(dev_priv, pipe) {
> >>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> >>>> -								  pipe);
> >>>> -
> >>>> -		if (!((1 << pipe) & crtc_mask))
> >>>> -			continue;
> >>>> -
> >>>> -		ret = drm_crtc_vblank_get(&crtc->base);
> >>>> -		if (WARN_ON(ret != 0)) {
> >>>> -			crtc_mask &= ~(1 << pipe);
> >>>> -			continue;
> >>>> -		}
> >>>> -
> >>>> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
> >>>> -	}
> >>>> -
> >>>> -	for_each_pipe(dev_priv, pipe) {
> >>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
> >>>> -								  pipe);
> >>>> -		long lret;
> >>>> -
> >>>> -		if (!((1 << pipe) & crtc_mask))
> >>>> -			continue;
> >>>> -
> >>>> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
> >>>> -				last_vblank_count[pipe] !=
> >>>> -					drm_crtc_vblank_count(&crtc->base),
> >>>> -				msecs_to_jiffies(50));
> >>>> -
> >>>> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
> >>>> -
> >>>> -		drm_crtc_vblank_put(&crtc->base);
> >>>> -	}
> >>>> -}
> >>>> -
> >>>> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
> >>>> -{
> >>>> -	/* fb updated, need to unpin old fb */
> >>>> -	if (crtc_state->fb_changed)
> >>>> -		return true;
> >>>> -
> >>>> -	/* wm changes, need vblank before final wm's */
> >>>> -	if (crtc_state->update_wm_post)
> >>>> -		return true;
> >>>> -
> >>>> -	if (crtc_state->wm.need_postvbl_update)
> >>>> -		return true;
> >>>> -
> >>>> -	return false;
> >>>> -}
> >>>> -
> >>>>  static void intel_update_crtc(struct drm_crtc *crtc,
> >>>>  			      struct drm_atomic_state *state,
> >>>>  			      struct drm_crtc_state *old_crtc_state,
> >>>> -			      struct drm_crtc_state *new_crtc_state,
> >>>> -			      unsigned int *crtc_vblank_mask)
> >>>> +			      struct drm_crtc_state *new_crtc_state)
> >>>>  {
> >>>>  	struct drm_device *dev = crtc->dev;
> >>>>  	struct drm_i915_private *dev_priv = to_i915(dev);
> >>>> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
> >>>>  	}
> >>>>  
> >>>>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
> >>>> -
> >>>> -	if (needs_vblank_wait(pipe_config))
> >>>> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
> >>>>  }
> >>>>  
> >>>> -static void intel_update_crtcs(struct drm_atomic_state *state,
> >>>> -			       unsigned int *crtc_vblank_mask)
> >>>> +static void intel_update_crtcs(struct drm_atomic_state *state)
> >>>>  {
> >>>>  	struct drm_crtc *crtc;
> >>>>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> >>>> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
> >>>>  			continue;
> >>>>  
> >>>>  		intel_update_crtc(crtc, state, old_crtc_state,
> >>>> -				  new_crtc_state, crtc_vblank_mask);
> >>>> +				  new_crtc_state);
> >>>>  	}
> >>>>  }
> >>>>  
> >>>> -static void skl_update_crtcs(struct drm_atomic_state *state,
> >>>> -			     unsigned int *crtc_vblank_mask)
> >>>> +static void skl_update_crtcs(struct drm_atomic_state *state)
> >>>>  {
> >>>>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
> >>>>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
> >>>> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
> >>>>  				vbl_wait = true;
> >>>>  
> >>>>  			intel_update_crtc(crtc, state, old_crtc_state,
> >>>> -					  new_crtc_state, crtc_vblank_mask);
> >>>> +					  new_crtc_state);
> >>>>  
> >>>>  			if (vbl_wait)
> >>>>  				intel_wait_for_vblank(dev_priv, pipe);
> >>>> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>>>  	struct intel_crtc_state *intel_cstate;
> >>>>  	bool hw_check = intel_state->modeset;
> >>>>  	u64 put_domains[I915_MAX_PIPES] = {};
> >>>> -	unsigned crtc_vblank_mask = 0;
> >>>>  	int i;
> >>>>  
> >>>>  	intel_atomic_commit_fence_wait(intel_state);
> >>>> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>>>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
> >>>>  
> >>>>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
> >>>> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
> >>>> +	dev_priv->display.update_crtcs(state);
> >>>>  
> >>>>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
> >>>>  	 * already, but still need the state for the delayed optimization. To
> >>>> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
> >>>>  	 *   we don't need out special handling any more.
> >>>>  	 */
> >>>>  	if (!state->legacy_cursor_update)
> >>> I think this if is dead code, since either we use the fastpath in
> >>> intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
> >>> then we clear this hack (on most platforms at least) since it just wreaks
> >>> complete havoc.
> >>>
> >>> We should probably do this logic in the async helpers, i.e. if async
> >>> helpers reject a cursor update, clear state->legacy_cursor_update.
> >>>
> >>> Since I think we don't necessarily need this patch in this series, I think
> >>> it'd be better in a separate series to straighten out the async_update
> >>> story. Which would then include moving i915 over to the core support.
> >> Erm, not sure what you mean here, isn't state->legacy_cursor_update mostly false, which means in most cases we'll wait?
> > Yup. And the few cases where it's true should be handled by the
> > fastpath/async plane update logic. Would allow us to ditch a few
> > conditions all over the code.
> > -Daniel
> 
> I've done some more tests, and it seems this patch is required.
> 
> Without this we may not always wait, and that could result in spurious failures of page flip with -EBUSY.
> I'd love to hide this from userspace, but I don't think that's possible yet. It's best to always call
> drm_atomic_helper_wait_for_flip_done() for now. It's a noop for legacy_cursor_update anyway.
> commit->flip_done is always set early.

I think we have a mistunderstanding here, I meant to say that you should
imo remove the if (state->legacy_cursor_update) check and call the
wait_for_flips_done helper unconditionally, not remove the call to the
function itself. That should work, and since we have a separate cursor
fastpath, also not cause troubles.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended.
  2017-09-02 18:59     ` [Intel-gfx] " Gustavo Padovan
@ 2017-09-04  7:31       ` Daniel Vetter
  0 siblings, 0 replies; 26+ messages in thread
From: Daniel Vetter @ 2017-09-04  7:31 UTC (permalink / raw)
  To: Gustavo Padovan; +Cc: intel-gfx, dri-devel

On Sat, Sep 02, 2017 at 03:59:22PM -0300, Gustavo Padovan wrote:
> 2017-08-30 Daniel Vetter <daniel@ffwll.ch>:
> 
> > On Wed, Aug 30, 2017 at 02:17:52PM +0200, Maarten Lankhorst wrote:
> > > By always keeping track of the last commit in plane_state, we know
> > > whether there is an active update on the plane or not. With that
> > > information we can reject the fast update, and force the slowpath
> > > to be used as was originally intended.
> > > 
> > > Cc: Gustavo Padovan <gustavo.padovan@collabora.com>
> > > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > 
> > Makes sense, but I think like patch 1 it would be better to do this in a
> > separate series. Which would then include a patch to move i915 over to the
> > async plane support.
> 
> This patch makes sense to me and it is better than the fix I wrote but never
> got around to send it out. I can pick in here locally, put the patches I
> have here for the drivers on top of it and send to intel-gfx for CI.
> 
> Anyway, without the i915 change, this is
> 
> Reviewed-by: Gustavo Padovan <gustavo.padovan@collabora.com>

I can supply the r-b for the i915 hunk :-)

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-09-04  7:30           ` Daniel Vetter
@ 2017-09-04  7:55             ` Maarten Lankhorst
  0 siblings, 0 replies; 26+ messages in thread
From: Maarten Lankhorst @ 2017-09-04  7:55 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, dri-devel

Op 04-09-17 om 09:30 schreef Daniel Vetter:
> On Thu, Aug 31, 2017 at 05:18:06PM +0200, Maarten Lankhorst wrote:
>> Op 30-08-17 om 15:40 schreef Daniel Vetter:
>>> On Wed, Aug 30, 2017 at 02:54:28PM +0200, Maarten Lankhorst wrote:
>>>> Op 30-08-17 om 14:43 schreef Daniel Vetter:
>>>>> On Wed, Aug 30, 2017 at 02:17:48PM +0200, Maarten Lankhorst wrote:
>>>>>> The next commit removes the wait for flip_done in in
>>>>>> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
>>>>>> to pass. Instead of using complicated vblank tracking which ends
>>>>>> up being ignored anyway, call the correct atomic helper. :)
>>>>>>
>>>>>> RFC because I'm not completely sure what we want to do with the vblank waiting,
>>>>>> I think for now this patch is the right way to go until we decide because it
>>>>>> preserves the status quo when drm_crtc_commit was introduced.
>>>>>>
>>>>>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
>>>>>> ---
>>>>>>  drivers/gpu/drm/i915/i915_drv.h      |  3 +-
>>>>>>  drivers/gpu/drm/i915/intel_display.c | 83 +++---------------------------------
>>>>>>  2 files changed, 8 insertions(+), 78 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>>>>>> index cbbafbfb0a55..de19621864a9 100644
>>>>>> --- a/drivers/gpu/drm/i915/i915_drv.h
>>>>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>>>>>> @@ -707,8 +707,7 @@ struct drm_i915_display_funcs {
>>>>>>  			    struct drm_atomic_state *old_state);
>>>>>>  	void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
>>>>>>  			     struct drm_atomic_state *old_state);
>>>>>> -	void (*update_crtcs)(struct drm_atomic_state *state,
>>>>>> -			     unsigned int *crtc_vblank_mask);
>>>>>> +	void (*update_crtcs)(struct drm_atomic_state *state);
>>>>>>  	void (*audio_codec_enable)(struct drm_connector *connector,
>>>>>>  				   struct intel_encoder *encoder,
>>>>>>  				   const struct drm_display_mode *adjusted_mode);
>>>>>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>>>>>> index 52c73b4dabaa..3f3cb96aa11e 100644
>>>>>> --- a/drivers/gpu/drm/i915/intel_display.c
>>>>>> +++ b/drivers/gpu/drm/i915/intel_display.c
>>>>>> @@ -12114,73 +12114,10 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
>>>>>>  	return dev->driver->get_vblank_counter(dev, crtc->pipe);
>>>>>>  }
>>>>>>  
>>>>>> -static void intel_atomic_wait_for_vblanks(struct drm_device *dev,
>>>>>> -					  struct drm_i915_private *dev_priv,
>>>>>> -					  unsigned crtc_mask)
>>>>>> -{
>>>>>> -	unsigned last_vblank_count[I915_MAX_PIPES];
>>>>>> -	enum pipe pipe;
>>>>>> -	int ret;
>>>>>> -
>>>>>> -	if (!crtc_mask)
>>>>>> -		return;
>>>>>> -
>>>>>> -	for_each_pipe(dev_priv, pipe) {
>>>>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>>>>>> -								  pipe);
>>>>>> -
>>>>>> -		if (!((1 << pipe) & crtc_mask))
>>>>>> -			continue;
>>>>>> -
>>>>>> -		ret = drm_crtc_vblank_get(&crtc->base);
>>>>>> -		if (WARN_ON(ret != 0)) {
>>>>>> -			crtc_mask &= ~(1 << pipe);
>>>>>> -			continue;
>>>>>> -		}
>>>>>> -
>>>>>> -		last_vblank_count[pipe] = drm_crtc_vblank_count(&crtc->base);
>>>>>> -	}
>>>>>> -
>>>>>> -	for_each_pipe(dev_priv, pipe) {
>>>>>> -		struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv,
>>>>>> -								  pipe);
>>>>>> -		long lret;
>>>>>> -
>>>>>> -		if (!((1 << pipe) & crtc_mask))
>>>>>> -			continue;
>>>>>> -
>>>>>> -		lret = wait_event_timeout(dev->vblank[pipe].queue,
>>>>>> -				last_vblank_count[pipe] !=
>>>>>> -					drm_crtc_vblank_count(&crtc->base),
>>>>>> -				msecs_to_jiffies(50));
>>>>>> -
>>>>>> -		WARN(!lret, "pipe %c vblank wait timed out\n", pipe_name(pipe));
>>>>>> -
>>>>>> -		drm_crtc_vblank_put(&crtc->base);
>>>>>> -	}
>>>>>> -}
>>>>>> -
>>>>>> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
>>>>>> -{
>>>>>> -	/* fb updated, need to unpin old fb */
>>>>>> -	if (crtc_state->fb_changed)
>>>>>> -		return true;
>>>>>> -
>>>>>> -	/* wm changes, need vblank before final wm's */
>>>>>> -	if (crtc_state->update_wm_post)
>>>>>> -		return true;
>>>>>> -
>>>>>> -	if (crtc_state->wm.need_postvbl_update)
>>>>>> -		return true;
>>>>>> -
>>>>>> -	return false;
>>>>>> -}
>>>>>> -
>>>>>>  static void intel_update_crtc(struct drm_crtc *crtc,
>>>>>>  			      struct drm_atomic_state *state,
>>>>>>  			      struct drm_crtc_state *old_crtc_state,
>>>>>> -			      struct drm_crtc_state *new_crtc_state,
>>>>>> -			      unsigned int *crtc_vblank_mask)
>>>>>> +			      struct drm_crtc_state *new_crtc_state)
>>>>>>  {
>>>>>>  	struct drm_device *dev = crtc->dev;
>>>>>>  	struct drm_i915_private *dev_priv = to_i915(dev);
>>>>>> @@ -12203,13 +12140,9 @@ static void intel_update_crtc(struct drm_crtc *crtc,
>>>>>>  	}
>>>>>>  
>>>>>>  	drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
>>>>>> -
>>>>>> -	if (needs_vblank_wait(pipe_config))
>>>>>> -		*crtc_vblank_mask |= drm_crtc_mask(crtc);
>>>>>>  }
>>>>>>  
>>>>>> -static void intel_update_crtcs(struct drm_atomic_state *state,
>>>>>> -			       unsigned int *crtc_vblank_mask)
>>>>>> +static void intel_update_crtcs(struct drm_atomic_state *state)
>>>>>>  {
>>>>>>  	struct drm_crtc *crtc;
>>>>>>  	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
>>>>>> @@ -12220,12 +12153,11 @@ static void intel_update_crtcs(struct drm_atomic_state *state,
>>>>>>  			continue;
>>>>>>  
>>>>>>  		intel_update_crtc(crtc, state, old_crtc_state,
>>>>>> -				  new_crtc_state, crtc_vblank_mask);
>>>>>> +				  new_crtc_state);
>>>>>>  	}
>>>>>>  }
>>>>>>  
>>>>>> -static void skl_update_crtcs(struct drm_atomic_state *state,
>>>>>> -			     unsigned int *crtc_vblank_mask)
>>>>>> +static void skl_update_crtcs(struct drm_atomic_state *state)
>>>>>>  {
>>>>>>  	struct drm_i915_private *dev_priv = to_i915(state->dev);
>>>>>>  	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
>>>>>> @@ -12284,7 +12216,7 @@ static void skl_update_crtcs(struct drm_atomic_state *state,
>>>>>>  				vbl_wait = true;
>>>>>>  
>>>>>>  			intel_update_crtc(crtc, state, old_crtc_state,
>>>>>> -					  new_crtc_state, crtc_vblank_mask);
>>>>>> +					  new_crtc_state);
>>>>>>  
>>>>>>  			if (vbl_wait)
>>>>>>  				intel_wait_for_vblank(dev_priv, pipe);
>>>>>> @@ -12346,7 +12278,6 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>>>  	struct intel_crtc_state *intel_cstate;
>>>>>>  	bool hw_check = intel_state->modeset;
>>>>>>  	u64 put_domains[I915_MAX_PIPES] = {};
>>>>>> -	unsigned crtc_vblank_mask = 0;
>>>>>>  	int i;
>>>>>>  
>>>>>>  	intel_atomic_commit_fence_wait(intel_state);
>>>>>> @@ -12438,7 +12369,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>>>  	pm_qos_update_request(&dev_priv->pm_qos_atomic, 0);
>>>>>>  
>>>>>>  	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
>>>>>> -	dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
>>>>>> +	dev_priv->display.update_crtcs(state);
>>>>>>  
>>>>>>  	/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
>>>>>>  	 * already, but still need the state for the delayed optimization. To
>>>>>> @@ -12450,7 +12381,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
>>>>>>  	 *   we don't need out special handling any more.
>>>>>>  	 */
>>>>>>  	if (!state->legacy_cursor_update)
>>>>> I think this if is dead code, since either we use the fastpath in
>>>>> intel_legacy_cursor_update, which means no atomic commit. Or we don't, and
>>>>> then we clear this hack (on most platforms at least) since it just wreaks
>>>>> complete havoc.
>>>>>
>>>>> We should probably do this logic in the async helpers, i.e. if async
>>>>> helpers reject a cursor update, clear state->legacy_cursor_update.
>>>>>
>>>>> Since I think we don't necessarily need this patch in this series, I think
>>>>> it'd be better in a separate series to straighten out the async_update
>>>>> story. Which would then include moving i915 over to the core support.
>>>> Erm, not sure what you mean here, isn't state->legacy_cursor_update mostly false, which means in most cases we'll wait?
>>> Yup. And the few cases where it's true should be handled by the
>>> fastpath/async plane update logic. Would allow us to ditch a few
>>> conditions all over the code.
>>> -Daniel
>> I've done some more tests, and it seems this patch is required.
>>
>> Without this we may not always wait, and that could result in spurious failures of page flip with -EBUSY.
>> I'd love to hide this from userspace, but I don't think that's possible yet. It's best to always call
>> drm_atomic_helper_wait_for_flip_done() for now. It's a noop for legacy_cursor_update anyway.
>> commit->flip_done is always set early.
> I think we have a mistunderstanding here, I meant to say that you should
> imo remove the if (state->legacy_cursor_update) check and call the
> wait_for_flips_done helper unconditionally, not remove the call to the
> function itself. That should work, and since we have a separate cursor
> fastpath, also not cause troubles.
True. The if is even a noop because setup_commit completes flip_done immediately..
Should be harmless to remove..

~Maarten
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [Intel-gfx] [RFC PATCH 1/5] drm/i915: Always wait for flip_done
  2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
  2017-08-30 12:43   ` Daniel Vetter
@ 2017-09-04 11:18   ` Chris Wilson
  1 sibling, 0 replies; 26+ messages in thread
From: Chris Wilson @ 2017-09-04 11:18 UTC (permalink / raw)
  To: Maarten Lankhorst, dri-devel; +Cc: intel-gfx

Quoting Maarten Lankhorst (2017-08-30 13:17:48)
> The next commit removes the wait for flip_done in in
> drm_atomic_helper_commit_cleanup_done, but we need it for the tests
> to pass. Instead of using complicated vblank tracking which ends
> up being ignored anyway, call the correct atomic helper. :)
> 
> RFC because I'm not completely sure what we want to do with the vblank waiting,
> I think for now this patch is the right way to go until we decide because it
> preserves the status quo when drm_crtc_commit was introduced.

Fwiw, this is fixing a bug in rotation:

> -static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
> -{
> -       /* fb updated, need to unpin old fb */
> -       if (crtc_state->fb_changed)
> -               return true;

is insufficient as the fb may be unchanged but a new vma allocated for
the rotated pages.
-Chris
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2017-09-04 11:18 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-30 12:17 [PATCH 0/5] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
2017-08-30 12:17 ` [RFC PATCH 1/5] drm/i915: Always wait for flip_done Maarten Lankhorst
2017-08-30 12:43   ` Daniel Vetter
2017-08-30 12:54     ` Maarten Lankhorst
2017-08-30 13:40       ` Daniel Vetter
2017-08-31 15:18         ` Maarten Lankhorst
2017-09-04  7:30           ` Daniel Vetter
2017-09-04  7:55             ` Maarten Lankhorst
2017-09-04 11:18   ` [Intel-gfx] " Chris Wilson
2017-08-30 12:17 ` [PATCH 2/5] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done Maarten Lankhorst
2017-08-30 12:28   ` Daniel Vetter
2017-08-30 12:17 ` [PATCH 3/5] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v2 Maarten Lankhorst
2017-08-30 13:09   ` Laurent Pinchart
2017-08-30 13:34     ` Maarten Lankhorst
2017-08-30 12:17 ` [PATCH 4/5] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v2 Maarten Lankhorst
2017-08-30 12:40   ` Daniel Vetter
2017-08-30 12:46     ` [Intel-gfx] " Maarten Lankhorst
2017-08-30 14:10   ` Laurent Pinchart
2017-08-30 14:17     ` Daniel Vetter
2017-08-30 14:44       ` [Intel-gfx] " Laurent Pinchart
2017-08-30 12:17 ` [RFC PATCH 5/5] drm/atomic: Make async plane update checks work as intended Maarten Lankhorst
2017-08-30 12:46   ` Daniel Vetter
2017-08-30 12:53     ` Maarten Lankhorst
2017-08-30 13:43       ` Daniel Vetter
2017-09-02 18:59     ` [Intel-gfx] " Gustavo Padovan
2017-09-04  7:31       ` Daniel Vetter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.