From: Tvrtko Ursulin <tvrtko.ursulin@linux.intel.com>
To: Chris Wilson <chris@chris-wilson.co.uk>, intel-gfx@lists.freedesktop.org
Cc: stable@vger.kernel.org
Subject: Re: [PATCH 2/3] drm/i915: Fix userptr deadlock with MAP_FIXED
Date: Wed, 01 Jul 2015 12:14:34 +0100 [thread overview]
Message-ID: <5593CB9A.5080401@linux.intel.com> (raw)
In-Reply-To: <1435683333-17844-2-git-send-email-chris@chris-wilson.co.uk>
On 06/30/2015 05:55 PM, Chris Wilson wrote:
> Michał Winiarski found a really evil way to trigger a struct_mutex
> deadlock with userptr. He found that if he allocated a userptr bo and
> then GTT mmaped another bo, or even itself, at the same address as the
> userptr using MAP_FIXED, he could then cause a deadlock any time we then
> had to invalidate the GTT mmappings (so at will).
>
> To counter act the deadlock, we make the observation that when the
> MAP_FIXED is made we would have an invalidate_range event for our
> object. After that we should no longer alias with the rogue mmapping. If
> we are then able to mark the object as no longer in use after the first
> invalidate, we do not need to grab the struct_mutex for the subsequent
> invalidations.
>
> The patch makes one eye-catching change. That is the removal serial=0
> after detecting a to-be-freed object inside the invalidate walker. I
> felt setting serial=0 was a questionable pessimisation: it denies us the
> chance to reuse the current iterator for the next loop (before it is
> freed) and being explicit makes the reader question the validity of the
> locking (since the object-free race could occur elsewhere). The
> serialisation of the iterator is through the spinlock, if the object is
> freed before the next loop then the notifier.serial will be incremented
> and we start the walk from the beginning as we detect the invalid cache.
>
> v2: Grammar fixes
> v3: Reorder set-active so that it is only set when obj->pages is set
> (and so needs cancellation). Only the order of setting obj->pages and
> the active-flag is crucial. Calling gup after invalidate-range begin
> means the userptr sees the new set of backing storage (and so will not
> need to invalidate its new pages), but we have to be careful not to set
> the active-flag prior to successfully establishing obj->pages.
>
> Reported-by: Michał Winiarski <michal.winiarski@intel.com>
> Testcase: igt/gem_userptr_blits/map-fixed*
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Michał Winiarski <michal.winiarski@intel.com>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> Cc: stable@vger.kernel.org
> ---
> drivers/gpu/drm/i915/i915_gem_userptr.c | 43 +++++++++++++++++++++++++++------
> 1 file changed, 36 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
> index cb367d9f7909..d3213fdefafc 100644
> --- a/drivers/gpu/drm/i915/i915_gem_userptr.c
> +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
> @@ -59,6 +59,7 @@ struct i915_mmu_object {
> struct interval_tree_node it;
> struct list_head link;
> struct drm_i915_gem_object *obj;
> + bool active;
> bool is_linear;
> };
>
> @@ -114,7 +115,8 @@ restart:
>
> obj = mo->obj;
>
> - if (!kref_get_unless_zero(&obj->base.refcount))
> + if (!mo->active ||
> + !kref_get_unless_zero(&obj->base.refcount))
> continue;
>
> spin_unlock(&mn->lock);
> @@ -151,7 +153,8 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
> else
> it = interval_tree_iter_first(&mn->objects, start, end);
> if (it != NULL) {
> - obj = container_of(it, struct i915_mmu_object, it)->obj;
> + struct i915_mmu_object *mo =
> + container_of(it, struct i915_mmu_object, it);
>
> /* The mmu_object is released late when destroying the
> * GEM object so it is entirely possible to gain a
> @@ -160,11 +163,9 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
> * the struct_mutex - and consequently use it after it
> * is freed and then double free it.
> */
> - if (!kref_get_unless_zero(&obj->base.refcount)) {
> - spin_unlock(&mn->lock);
> - serial = 0;
> - continue;
> - }
> + if (mo->active &&
> + kref_get_unless_zero(&mo->obj->base.refcount))
> + obj = mo->obj;
>
> serial = mn->serial;
> }
> @@ -546,6 +547,30 @@ err:
> }
>
> static void
> +__i915_gem_userptr_set_active(struct drm_i915_gem_object *obj,
> + bool value)
> +{
> + /* During mm_invalidate_range we need to cancel any userptr that
> + * overlaps the range being invalidated. Doing so requires the
> + * struct_mutex, and that risks recursion. In order to cause
> + * recursion, the user must alias the userptr address space with
> + * a GTT mmapping (possible with a MAP_FIXED) - then when we have
> + * to invalidate that mmaping, mm_invalidate_range is called with
> + * the userptr address *and* the struct_mutex held. To prevent that
> + * we set a flag under the i915_mmu_notifier spinlock to indicate
> + * whether this object is valid.
> + */
> +#if defined(CONFIG_MMU_NOTIFIER)
> + if (obj->userptr.mmu_object == NULL)
> + return;
> +
> + spin_lock(&obj->userptr.mmu_object->mn->lock);
> + obj->userptr.mmu_object->active = value;
> + spin_unlock(&obj->userptr.mmu_object->mn->lock);
> +#endif
Would this be more obvious as atomic_t? Since I suspect spinlock is just
for the memory barrier, if that. Hm.. What if we get invalidate while
get_pages is running, before it set active but after gup has succeeded?
> +
> +static void
> __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
> {
> struct get_pages_work *work = container_of(_work, typeof(*work), work);
> @@ -585,6 +610,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
> if (pinned == num_pages) {
> ret = st_set_pages(&obj->pages, pvec, num_pages);
> if (ret == 0) {
> + __i915_gem_userptr_set_active(obj, true);
> list_add_tail(&obj->global_list, &to_i915(dev)->mm.unbound_list);
> pinned = 0;
> }
> @@ -699,6 +725,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
> } else {
> ret = st_set_pages(&obj->pages, pvec, num_pages);
> if (ret == 0) {
> + __i915_gem_userptr_set_active(obj, true);
> obj->userptr.work = NULL;
> pinned = 0;
> }
> @@ -732,6 +759,8 @@ i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj)
>
> sg_free_table(obj->pages);
> kfree(obj->pages);
> +
> + __i915_gem_userptr_set_active(obj, false);
> }
>
> static void
>
Regards,
Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx
next prev parent reply other threads:[~2015-07-01 11:14 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-06-29 10:59 [PATCH] tests/gem_userptr_blits: subtests for MAP_FIXED mappings of regular bo Michał Winiarski
2015-06-29 11:09 ` [PATCH] drm/i915: Fix userptr deadlock with MAP_FIXED Chris Wilson
2015-06-29 11:17 ` [PATCH v2] " Chris Wilson
2015-06-29 15:57 ` Michał Winiarski
2015-06-30 14:52 ` Tvrtko Ursulin
2015-06-30 15:31 ` [Intel-gfx] " Chris Wilson
2015-06-30 15:47 ` Chris Wilson
2015-06-29 14:01 ` [PATCH] tests/gem_userptr_blits: subtests for MAP_FIXED mappings of regular bo Tvrtko Ursulin
2015-06-29 14:07 ` Chris Wilson
2015-06-29 14:15 ` Tvrtko Ursulin
2015-06-29 14:25 ` Chris Wilson
2015-06-29 14:56 ` Tvrtko Ursulin
2015-06-29 15:03 ` Chris Wilson
2015-06-30 15:01 ` [PATCH v2] tests/gem_userptr_blits: subtests for MAP_FIXED mappings of regular BO Michał Winiarski
2015-06-30 16:55 ` [PATCH 1/3] drm/i915: Only update the current userptr worker Chris Wilson
2015-06-30 16:55 ` [PATCH 2/3] drm/i915: Fix userptr deadlock with MAP_FIXED Chris Wilson
2015-07-01 11:14 ` Tvrtko Ursulin [this message]
2015-07-01 11:29 ` [Intel-gfx] " Chris Wilson
2015-06-30 16:55 ` [PATCH 3/3] drm/i915: Use a task to cancel the userptr on invalidate_range Chris Wilson
2015-07-01 12:56 ` Tvrtko Ursulin
2015-07-03 11:00 ` Chris Wilson
2015-07-02 16:40 ` shuang.he
2015-07-03 11:03 ` [PATCH] " Chris Wilson
2015-07-01 9:48 ` [PATCH 1/3] drm/i915: Only update the current userptr worker Tvrtko Ursulin
2015-07-01 9:59 ` Chris Wilson
2015-07-01 10:58 ` Tvrtko Ursulin
2015-07-01 11:09 ` Chris Wilson
2015-07-01 12:26 ` Tvrtko Ursulin
2015-07-01 13:11 ` Chris Wilson
2015-07-03 10:48 ` Michał Winiarski
2015-07-03 10:53 ` Chris Wilson
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=5593CB9A.5080401@linux.intel.com \
--to=tvrtko.ursulin@linux.intel.com \
--cc=chris@chris-wilson.co.uk \
--cc=intel-gfx@lists.freedesktop.org \
--cc=stable@vger.kernel.org \
/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