All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] drm/i915: Embrace the race in busy-ioctl
@ 2016-08-12 17:52 Chris Wilson
  2016-08-13  5:23 ` ✗ Ro.CI.BAT: failure for " Patchwork
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Chris Wilson @ 2016-08-12 17:52 UTC (permalink / raw)
  To: intel-gfx; +Cc: Daniel Vetter

Daniel Vetter proposed a new challenge to the serialisation inside the
busy-ioctl that exposed a flaw that could result in us reporting the
wrong engine as being busy. If the request is reallocated as we test
its busyness and then reassigned to this object by another thread, we
would not notice that the test itself was incorrect.

We are faced with a choice of using __i915_gem_active_get_request_rcu()
to first acquire a reference to the request preventing the race, or to
acknowledge the race and accept the limitations upon the accuracy of the
busy flags. Note that we guarantee that we never falsely report the
object as idle (providing userspace itself doesn't race), and so the
most important use of the busy-ioctl and its guarantees are fulfilled.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_gem.c | 87 ++++++++++++++++++++++-------------------
 include/uapi/drm/i915_drm.h     | 15 ++++++-
 2 files changed, 60 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 5566916870eb..c77915378768 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3791,49 +3791,54 @@ static __always_inline unsigned int
 __busy_set_if_active(const struct i915_gem_active *active,
 		     unsigned int (*flag)(unsigned int id))
 {
-	/* For more discussion about the barriers and locking concerns,
-	 * see __i915_gem_active_get_rcu().
-	 */
-	do {
-		struct drm_i915_gem_request *request;
-		unsigned int id;
-
-		request = rcu_dereference(active->request);
-		if (!request || i915_gem_request_completed(request))
-			return 0;
+	struct drm_i915_gem_request *request;
 
-		id = request->engine->exec_id;
+	request = rcu_dereference(active->request);
+	if (!request || i915_gem_request_completed(request))
+		return 0;
 
-		/* Check that the pointer wasn't reassigned and overwritten.
-		 *
-		 * In __i915_gem_active_get_rcu(), we enforce ordering between
-		 * the first rcu pointer dereference (imposing a
-		 * read-dependency only on access through the pointer) and
-		 * the second lockless access through the memory barrier
-		 * following a successful atomic_inc_not_zero(). Here there
-		 * is no such barrier, and so we must manually insert an
-		 * explicit read barrier to ensure that the following
-		 * access occurs after all the loads through the first
-		 * pointer.
-		 *
-		 * It is worth comparing this sequence with
-		 * raw_write_seqcount_latch() which operates very similarly.
-		 * The challenge here is the visibility of the other CPU
-		 * writes to the reallocated request vs the local CPU ordering.
-		 * Before the other CPU can overwrite the request, it will
-		 * have updated our active->request and gone through a wmb.
-		 * During the read here, we want to make sure that the values
-		 * we see have not been overwritten as we do so - and we do
-		 * that by serialising the second pointer check with the writes
-		 * on other other CPUs.
-		 *
-		 * The corresponding write barrier is part of
-		 * rcu_assign_pointer().
-		 */
-		smp_rmb();
-		if (request == rcu_access_pointer(active->request))
-			return flag(id);
-	} while (1);
+	/* This is racy. See __i915_gem_active_get_rcu() for a in detail
+	 * discussion of how to handle the race correctly, but for reporting
+	 * the busy state we err on the side of potentially reporting the
+	 * wrong engine as being busy (but we guarantee that the result
+	 * is at least self-consistent).
+	 *
+	 * As we use SLAB_DESTROY_BY_RCU, the request may be reallocated
+	 * whilst we are inspecting it, even under the RCU read lock as we are.
+	 * This means that there is a small window for the engine and/or the
+	 * seqno to have been overwritten. The seqno will always be in the
+	 * future compared to the intended, and so we know that if that
+	 * seqno is idle (on whatever engine) our request is idle and the
+	 * return 0 above is correct.
+	 *
+	 * The issue is that if the engine is switched, it is just as likely
+	 * to report that it is busy (but since the switch happened, we know
+	 * the request should be idle). So there is a small chance that a busy
+	 * result is actually the wrong engine.
+	 *
+	 * So why don't we care?
+	 *
+	 * For starters, the busy ioctl is a heuristic that is by definition
+	 * racy. Even with perfect serialisation in the driver, the hardware
+	 * state is constantly advancing - the state we report to the user
+	 * is stale.
+	 *
+	 * The critical information for the busy-ioctl is whether the object
+	 * is idle as userspace relies on that to detect whether its next
+	 * access will stall, or if it has missed submitting commands to
+	 * the hardware allowing the GPU to stall. We never generate a
+	 * false-positive for idleness, thus busy-ioctl is reliable at the
+	 * most fundamental level, and we maintain the guarantee that a
+	 * busy object left to itself will eventually become idle (and stay
+	 * idle!).
+	 *
+	 * We allow ourselves the leeway of potentially misreporting the busy
+	 * state because that is an optimisation heuristic that is constantly
+	 * in flux. Being quickly able to detect the busy/idle state is more
+	 * much important than accurate logging of which engines exactly were
+	 * busy.
+	 */
+	return flag(request->engine->exec_id);
 }
 
 static __always_inline unsigned int
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 452629de7a57..bfd6d59d5099 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -855,7 +855,15 @@ struct drm_i915_gem_busy {
 	 * having flushed any pending activity), and a non-zero return that
 	 * the object is still in-flight on the GPU. (The GPU has not yet
 	 * signaled completion for all pending requests that reference the
-	 * object.)
+	 * object.) An object is guaranteed to become idle eventually (so
+	 * long as new GPU commands are executed upon it). Due to the
+	 * asynchronous natutre of the hardware, an object reported
+	 * as busy may become idle before the ioctl is completed.
+	 *
+	 * Furthermore, if the object is busy, which engine is busy is only
+	 * provided as a guide. There are race conditions which prevent the
+	 * report from being always accurate. However, the converse is not
+	 * true, if the object is idle the result is accurate.
 	 *
 	 * The returned dword is split into two fields to indicate both
 	 * the engines on which the object is being read, and the
@@ -878,6 +886,11 @@ struct drm_i915_gem_busy {
 	 * execution engines, e.g. multiple media engines, which are
 	 * mapped to the same identifier in the EXECBUFFER2 ioctl and
 	 * so are not separately reported for busyness.
+	 *
+	 * Caveat emptor:
+	 * Only the boolean result of this query is reliable; that is whether
+	 * the object is idle or busy. The report of which engines are busy
+	 * should be only used as a heuristic.
 	 */
 	__u32 busy;
 };
-- 
2.8.1

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

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

* ✗ Ro.CI.BAT: failure for drm/i915: Embrace the race in busy-ioctl
  2016-08-12 17:52 [PATCH] drm/i915: Embrace the race in busy-ioctl Chris Wilson
@ 2016-08-13  5:23 ` Patchwork
  2016-08-15 12:48 ` [PATCH] " Joonas Lahtinen
  2016-08-15 13:06 ` Mika Kuoppala
  2 siblings, 0 replies; 4+ messages in thread
From: Patchwork @ 2016-08-13  5:23 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: drm/i915: Embrace the race in busy-ioctl
URL   : https://patchwork.freedesktop.org/series/11034/
State : failure

== Summary ==

Series 11034v1 drm/i915: Embrace the race in busy-ioctl
http://patchwork.freedesktop.org/api/1.0/series/11034/revisions/1/mbox

Test kms_cursor_legacy:
        Subgroup basic-cursor-vs-flip-legacy:
                pass       -> FAIL       (ro-byt-n2820)
        Subgroup basic-cursor-vs-flip-varying-size:
                fail       -> PASS       (ro-ilk1-i5-650)
        Subgroup basic-flip-vs-cursor-legacy:
                fail       -> PASS       (ro-skl3-i5-6260u)
        Subgroup basic-flip-vs-cursor-varying-size:
                pass       -> FAIL       (ro-bdw-i5-5250u)
                pass       -> DMESG-FAIL (fi-skl-i7-6700k)
Test kms_pipe_crc_basic:
        Subgroup suspend-read-crc-pipe-a:
                dmesg-warn -> PASS       (ro-bdw-i7-5600u)
                dmesg-warn -> SKIP       (ro-bdw-i5-5250u)
        Subgroup suspend-read-crc-pipe-c:
                skip       -> DMESG-WARN (ro-bdw-i5-5250u)

fi-hsw-i7-4770k  total:244  pass:222  dwarn:0   dfail:0   fail:0   skip:22 
fi-kbl-qkkr      total:244  pass:186  dwarn:28  dfail:0   fail:3   skip:27 
fi-skl-i7-6700k  total:244  pass:208  dwarn:4   dfail:2   fail:2   skip:28 
fi-snb-i7-2600   total:244  pass:202  dwarn:0   dfail:0   fail:0   skip:42 
ro-bdw-i5-5250u  total:240  pass:219  dwarn:2   dfail:0   fail:1   skip:18 
ro-bdw-i7-5600u  total:240  pass:207  dwarn:0   dfail:0   fail:1   skip:32 
ro-bsw-n3050     total:240  pass:194  dwarn:0   dfail:0   fail:4   skip:42 
ro-byt-n2820     total:240  pass:196  dwarn:0   dfail:0   fail:4   skip:40 
ro-hsw-i3-4010u  total:240  pass:214  dwarn:0   dfail:0   fail:0   skip:26 
ro-hsw-i7-4770r  total:240  pass:185  dwarn:0   dfail:0   fail:0   skip:55 
ro-ilk1-i5-650   total:235  pass:174  dwarn:0   dfail:0   fail:1   skip:60 
ro-ivb-i7-3770   total:240  pass:205  dwarn:0   dfail:0   fail:0   skip:35 
ro-ivb2-i7-3770  total:240  pass:209  dwarn:0   dfail:0   fail:0   skip:31 
ro-skl3-i5-6260u total:240  pass:223  dwarn:0   dfail:0   fail:3   skip:14 

Results at /archive/results/CI_IGT_test/RO_Patchwork_1857/

3612906 drm-intel-nightly: 2016y-08m-12d-15h-08m-02s UTC integration manifest
eb6c27a drm/i915: Embrace the race in busy-ioctl

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

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

* Re: [PATCH] drm/i915: Embrace the race in busy-ioctl
  2016-08-12 17:52 [PATCH] drm/i915: Embrace the race in busy-ioctl Chris Wilson
  2016-08-13  5:23 ` ✗ Ro.CI.BAT: failure for " Patchwork
@ 2016-08-15 12:48 ` Joonas Lahtinen
  2016-08-15 13:06 ` Mika Kuoppala
  2 siblings, 0 replies; 4+ messages in thread
From: Joonas Lahtinen @ 2016-08-15 12:48 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: Daniel Vetter

On pe, 2016-08-12 at 18:52 +0100, Chris Wilson wrote:
> Daniel Vetter proposed a new challenge to the serialisation inside the
> busy-ioctl that exposed a flaw that could result in us reporting the
> wrong engine as being busy. If the request is reallocated as we test
> its busyness and then reassigned to this object by another thread, we
> would not notice that the test itself was incorrect.
> 
> We are faced with a choice of using __i915_gem_active_get_request_rcu()
> to first acquire a reference to the request preventing the race, or to
> acknowledge the race and accept the limitations upon the accuracy of the
> busy flags. Note that we guarantee that we never falsely report the
> object as idle (providing userspace itself doesn't race), and so the
> most important use of the busy-ioctl and its guarantees are fulfilled.
> 

If Daniel acks the userspace change,

Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>

Regards, Joonas
-- 
Joonas Lahtinen
Open Source Technology Center
Intel Corporation
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH] drm/i915: Embrace the race in busy-ioctl
  2016-08-12 17:52 [PATCH] drm/i915: Embrace the race in busy-ioctl Chris Wilson
  2016-08-13  5:23 ` ✗ Ro.CI.BAT: failure for " Patchwork
  2016-08-15 12:48 ` [PATCH] " Joonas Lahtinen
@ 2016-08-15 13:06 ` Mika Kuoppala
  2 siblings, 0 replies; 4+ messages in thread
From: Mika Kuoppala @ 2016-08-15 13:06 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: Daniel Vetter

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Daniel Vetter proposed a new challenge to the serialisation inside the
> busy-ioctl that exposed a flaw that could result in us reporting the
> wrong engine as being busy. If the request is reallocated as we test
> its busyness and then reassigned to this object by another thread, we
> would not notice that the test itself was incorrect.
>
> We are faced with a choice of using __i915_gem_active_get_request_rcu()
> to first acquire a reference to the request preventing the race, or to
> acknowledge the race and accept the limitations upon the accuracy of the
> busy flags. Note that we guarantee that we never falsely report the
> object as idle (providing userspace itself doesn't race), and so the
> most important use of the busy-ioctl and its guarantees are fulfilled.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/i915_gem.c | 87 ++++++++++++++++++++++-------------------
>  include/uapi/drm/i915_drm.h     | 15 ++++++-
>  2 files changed, 60 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 5566916870eb..c77915378768 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -3791,49 +3791,54 @@ static __always_inline unsigned int
>  __busy_set_if_active(const struct i915_gem_active *active,
>  		     unsigned int (*flag)(unsigned int id))
>  {
> -	/* For more discussion about the barriers and locking concerns,
> -	 * see __i915_gem_active_get_rcu().
> -	 */
> -	do {
> -		struct drm_i915_gem_request *request;
> -		unsigned int id;
> -
> -		request = rcu_dereference(active->request);
> -		if (!request || i915_gem_request_completed(request))
> -			return 0;
> +	struct drm_i915_gem_request *request;
>  
> -		id = request->engine->exec_id;
> +	request = rcu_dereference(active->request);
> +	if (!request || i915_gem_request_completed(request))
> +		return 0;
>  
> -		/* Check that the pointer wasn't reassigned and overwritten.
> -		 *
> -		 * In __i915_gem_active_get_rcu(), we enforce ordering between
> -		 * the first rcu pointer dereference (imposing a
> -		 * read-dependency only on access through the pointer) and
> -		 * the second lockless access through the memory barrier
> -		 * following a successful atomic_inc_not_zero(). Here there
> -		 * is no such barrier, and so we must manually insert an
> -		 * explicit read barrier to ensure that the following
> -		 * access occurs after all the loads through the first
> -		 * pointer.
> -		 *
> -		 * It is worth comparing this sequence with
> -		 * raw_write_seqcount_latch() which operates very similarly.
> -		 * The challenge here is the visibility of the other CPU
> -		 * writes to the reallocated request vs the local CPU ordering.
> -		 * Before the other CPU can overwrite the request, it will
> -		 * have updated our active->request and gone through a wmb.
> -		 * During the read here, we want to make sure that the values
> -		 * we see have not been overwritten as we do so - and we do
> -		 * that by serialising the second pointer check with the writes
> -		 * on other other CPUs.
> -		 *
> -		 * The corresponding write barrier is part of
> -		 * rcu_assign_pointer().
> -		 */
> -		smp_rmb();
> -		if (request == rcu_access_pointer(active->request))
> -			return flag(id);
> -	} while (1);
> +	/* This is racy. See __i915_gem_active_get_rcu() for a in detail
> +	 * discussion of how to handle the race correctly, but for reporting
> +	 * the busy state we err on the side of potentially reporting the
> +	 * wrong engine as being busy (but we guarantee that the result
> +	 * is at least self-consistent).
> +	 *
> +	 * As we use SLAB_DESTROY_BY_RCU, the request may be reallocated
> +	 * whilst we are inspecting it, even under the RCU read lock as we are.
> +	 * This means that there is a small window for the engine and/or the
> +	 * seqno to have been overwritten. The seqno will always be in the
> +	 * future compared to the intended, and so we know that if that
> +	 * seqno is idle (on whatever engine) our request is idle and the
> +	 * return 0 above is correct.
> +	 *
> +	 * The issue is that if the engine is switched, it is just as likely
> +	 * to report that it is busy (but since the switch happened, we know
> +	 * the request should be idle). So there is a small chance that a busy
> +	 * result is actually the wrong engine.
> +	 *
> +	 * So why don't we care?
> +	 *
> +	 * For starters, the busy ioctl is a heuristic that is by definition
> +	 * racy. Even with perfect serialisation in the driver, the hardware
> +	 * state is constantly advancing - the state we report to the user
> +	 * is stale.
> +	 *
> +	 * The critical information for the busy-ioctl is whether the object
> +	 * is idle as userspace relies on that to detect whether its next
> +	 * access will stall, or if it has missed submitting commands to
> +	 * the hardware allowing the GPU to stall. We never generate a
> +	 * false-positive for idleness, thus busy-ioctl is reliable at the
> +	 * most fundamental level, and we maintain the guarantee that a
> +	 * busy object left to itself will eventually become idle (and stay
> +	 * idle!).
> +	 *
> +	 * We allow ourselves the leeway of potentially misreporting the busy
> +	 * state because that is an optimisation heuristic that is constantly
> +	 * in flux. Being quickly able to detect the busy/idle state is more
> +	 * much important than accurate logging of which engines exactly were
> +	 * busy.
> +	 */
> +	return flag(request->engine->exec_id);
>  }
>  
>  static __always_inline unsigned int
> diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
> index 452629de7a57..bfd6d59d5099 100644
> --- a/include/uapi/drm/i915_drm.h
> +++ b/include/uapi/drm/i915_drm.h
> @@ -855,7 +855,15 @@ struct drm_i915_gem_busy {
>  	 * having flushed any pending activity), and a non-zero return that
>  	 * the object is still in-flight on the GPU. (The GPU has not yet
>  	 * signaled completion for all pending requests that reference the
> -	 * object.)
> +	 * object.) An object is guaranteed to become idle eventually (so
> +	 * long as new GPU commands are executed upon it). Due to the
> +	 * asynchronous natutre of the hardware, an object reported

s/natutre/nature.

> +	 * as busy may become idle before the ioctl is completed.
> +	 *
> +	 * Furthermore, if the object is busy, which engine is busy is only
> +	 * provided as a guide. There are race conditions which prevent the
> +	 * report from being always accurate. However, the converse is not
> +	 * true, if the object is idle the result is accurate.
>  	 *

In here I am thinking accurate in busyness wise or accurate in busyness
and engine wise. Perhaps explicitly state which values can be trusted in
which case of busyness.

Overall the comments are sterling as this is very hard to undestand
area. Thanks.

Joonas already stamped but you can also add
Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com>


>  	 * The returned dword is split into two fields to indicate both
>  	 * the engines on which the object is being read, and the
> @@ -878,6 +886,11 @@ struct drm_i915_gem_busy {
>  	 * execution engines, e.g. multiple media engines, which are
>  	 * mapped to the same identifier in the EXECBUFFER2 ioctl and
>  	 * so are not separately reported for busyness.
> +	 *
> +	 * Caveat emptor:
> +	 * Only the boolean result of this query is reliable; that is whether
> +	 * the object is idle or busy. The report of which engines are busy
> +	 * should be only used as a heuristic.
>  	 */
>  	__u32 busy;
>  };
> -- 
> 2.8.1
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

end of thread, other threads:[~2016-08-15 13:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-12 17:52 [PATCH] drm/i915: Embrace the race in busy-ioctl Chris Wilson
2016-08-13  5:23 ` ✗ Ro.CI.BAT: failure for " Patchwork
2016-08-15 12:48 ` [PATCH] " Joonas Lahtinen
2016-08-15 13:06 ` Mika Kuoppala

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.