aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeo (Sunpeng) Li <sunpeng.li@amd.com>2018-01-17 06:51:08 -0500
committerMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2018-02-01 05:14:06 -0500
commit54f809cfbd6b4a43959039f5d33596ed3297ce16 (patch)
tree3a43f1415e4ed9f29f70c0a1f057c9988660ec88
parent745fd50f3b044db6a3922e1718306555613164b0 (diff)
drm/atomic: Fix memleak on ERESTARTSYS during non-blocking commits
During a non-blocking commit, it is possible to return before the commit_tail work is queued (-ERESTARTSYS, for example). Since a reference on the crtc commit object is obtained for the pending vblank event when preparing the commit, the above situation will leave us with an extra reference. Therefore, if the commit_tail worker has not consumed the event at the end of a commit, release it's reference. Changes since v1: - Also check for state->event->base.completion being set, to handle the case where stall_checks() fails in setup_crtc_commit(). Changes since v2: - Add a flag to drm_crtc_commit, to prevent dereferencing a freed event. i915 may unreference the state in a worker. Fixes: 24835e442f28 ("drm: reference count event->completion") Cc: <stable@vger.kernel.org> # v4.11+ Signed-off-by: Leo (Sunpeng) Li <sunpeng.li@amd.com> Acked-by: Harry Wentland <harry.wentland@amd.com> #v1 Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20180117115108.29608-1-maarten.lankhorst@linux.intel.com Reviewed-by: Sean Paul <seanpaul@chromium.org>
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c15
-rw-r--r--include/drm/drm_atomic.h9
2 files changed, 24 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index b16f1d69a0bb..e8c249361d7e 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1778,6 +1778,8 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
1778 new_crtc_state->event->base.completion = &commit->flip_done; 1778 new_crtc_state->event->base.completion = &commit->flip_done;
1779 new_crtc_state->event->base.completion_release = release_crtc_commit; 1779 new_crtc_state->event->base.completion_release = release_crtc_commit;
1780 drm_crtc_commit_get(commit); 1780 drm_crtc_commit_get(commit);
1781
1782 commit->abort_completion = true;
1781 } 1783 }
1782 1784
1783 for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) { 1785 for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
@@ -3327,8 +3329,21 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
3327void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) 3329void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
3328{ 3330{
3329 if (state->commit) { 3331 if (state->commit) {
3332 /*
3333 * In the event that a non-blocking commit returns
3334 * -ERESTARTSYS before the commit_tail work is queued, we will
3335 * have an extra reference to the commit object. Release it, if
3336 * the event has not been consumed by the worker.
3337 *
3338 * state->event may be freed, so we can't directly look at
3339 * state->event->base.completion.
3340 */
3341 if (state->event && state->commit->abort_completion)
3342 drm_crtc_commit_put(state->commit);
3343
3330 kfree(state->commit->event); 3344 kfree(state->commit->event);
3331 state->commit->event = NULL; 3345 state->commit->event = NULL;
3346
3332 drm_crtc_commit_put(state->commit); 3347 drm_crtc_commit_put(state->commit);
3333 } 3348 }
3334 3349
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 5afd6e364fb6..c63b0b48e884 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -134,6 +134,15 @@ struct drm_crtc_commit {
134 * &drm_pending_vblank_event pointer to clean up private events. 134 * &drm_pending_vblank_event pointer to clean up private events.
135 */ 135 */
136 struct drm_pending_vblank_event *event; 136 struct drm_pending_vblank_event *event;
137
138 /**
139 * @abort_completion:
140 *
141 * A flag that's set after drm_atomic_helper_setup_commit takes a second
142 * reference for the completion of $drm_crtc_state.event. It's used by
143 * the free code to remove the second reference if commit fails.
144 */
145 bool abort_completion;
137}; 146};
138 147
139struct __drm_planes_state { 148struct __drm_planes_state {