From: Daniel Vetter <daniel@ffwll.ch>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v4.
Date: Tue, 5 Sep 2017 08:25:29 +0200 [thread overview]
Message-ID: <20170905062529.qtl2tx6b3hxpflea@phenom.ffwll.local> (raw)
In-Reply-To: <20170904150456.31049-1-maarten.lankhorst@linux.intel.com>
On Mon, Sep 04, 2017 at 05:04:56PM +0200, 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)
> Changes since v2:
> - Remove drm_atomic_helper_async_check hunk. (pinchartl)
> Changes since v3:
> - Fix use-after-free in drm_atomic_helper_commit_cleanup_done().
>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Might be good to drop this, or at least annoate that the r-b is for v3,
just for next time around. But I looked at the patch again, r-b: me still
holds I think. But then I missed the bug in v4 ...
-Daniel
> ---
> drivers/gpu/drm/drm_atomic.c | 7 ----
> drivers/gpu/drm/drm_atomic_helper.c | 82 ++++++++++++++++---------------------
> include/drm/drm_atomic.h | 1 -
> include/drm/drm_crtc.h | 23 +++++++++--
> 4 files changed, 54 insertions(+), 59 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 4e53aae9a1fb..80c138cbde9a 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)
> @@ -1731,7 +1731,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 +1769,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 +1784,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 +1808,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);
> @@ -1852,15 +1830,25 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
> void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
> {
> struct drm_crtc *crtc;
> - struct drm_crtc_state *new_crtc_state;
> + struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> struct drm_crtc_commit *commit;
> int i;
>
> - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
> - commit = old_state->crtcs[i].commit;
> + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
> + commit = new_crtc_state->commit;
> if (!commit)
> continue;
>
> + /*
> + * copy new_crtc_state->commit to old_crtc_state->commit,
> + * it's unsafe to touch new_crtc_state after hw_done,
> + * but we still need to do so in cleanup_done().
> + */
> + if (old_crtc_state->commit)
> + drm_crtc_commit_put(old_crtc_state->commit);
> +
> + old_crtc_state->commit = drm_crtc_commit_get(commit);
> +
> /* backend must have consumed any event by now */
> WARN_ON(new_crtc_state->event);
> complete_all(&commit->hw_done);
> @@ -1882,13 +1870,13 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
> void drm_atomic_helper_commit_cleanup_done(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) {
> - commit = old_state->crtcs[i].commit;
> + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
> + commit = old_crtc_state->commit;
> if (WARN_ON(!commit))
> continue;
>
> @@ -2294,20 +2282,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;
> }
> @@ -2332,13 +2313,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;
> }
> }
>
> @@ -3186,6 +3167,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;
> }
> @@ -3224,6 +3206,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 f73b663c1f76..285fbc4ec360 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
>
> _______________________________________________
> 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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
next prev parent reply other threads:[~2017-09-05 6:25 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-09-04 10:48 [PATCH v2 0/6] drm/atomic: Fix use-after-free with unbound connectors/planes Maarten Lankhorst
2017-09-04 10:48 ` [PATCH v2 1/6] drm/i915: Always wait for flip_done, v2 Maarten Lankhorst
2017-09-07 9:49 ` Daniel Vetter
2017-09-08 9:08 ` Maarten Lankhorst
2017-09-04 10:48 ` [PATCH v2 2/6] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v3 Maarten Lankhorst
2017-09-04 15:04 ` [PATCH] drm/atomic: Move drm_crtc_commit to drm_crtc_state, v4 Maarten Lankhorst
2017-09-04 18:01 ` kbuild test robot
2017-09-05 6:25 ` Daniel Vetter [this message]
2017-09-04 10:48 ` [PATCH v2 3/6] drm/atomic: Remove waits in drm_atomic_helper_commit_cleanup_done, v2 Maarten Lankhorst
2017-09-04 10:48 ` [PATCH v2 4/6] drm/atomic: Return commit in drm_crtc_commit_get for better annotation Maarten Lankhorst
2017-09-07 9:59 ` Daniel Vetter
2017-09-04 10:48 ` [PATCH v2 5/6] drm/atomic: Fix freeing connector/plane state too early by tracking commits, v3 Maarten Lankhorst
2017-09-07 10:05 ` Daniel Vetter
2017-09-07 11:08 ` Maarten Lankhorst
2017-09-04 10:48 ` [PATCH v2 6/6] drm/atomic: Make async plane update checks work as intended, v2 Maarten Lankhorst
[not found] ` <20170904104838.23822-7-maarten.lankhorst-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-09-24 14:33 ` Dmitry Osipenko
[not found] ` <54467116-df02-e6ad-ac14-90aa79e164e4-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2017-09-25 6:43 ` [PATCH] drm/atomic: Make async plane update checks actually work as intended Maarten Lankhorst
[not found] ` <a7d5fb04-c2f5-b743-5940-a1bd181d780d-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-09-25 14:08 ` Dmitry Osipenko
2017-09-26 4:59 ` Daniel Vetter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170905062529.qtl2tx6b3hxpflea@phenom.ffwll.local \
--to=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=intel-gfx@lists.freedesktop.org \
--cc=maarten.lankhorst@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox