diff mbox series

[10/10] drm/i915: Flush idle barriers when waiting

Message ID 20191010071434.31195-10-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show
Series [01/10] drm/i915: Note the addition of timeslicing to the pretend scheduler | expand

Commit Message

Chris Wilson Oct. 10, 2019, 7:14 a.m. UTC
If we do find ourselves with an idle barrier inside our active while
waiting, attempt to flush it by emitting a pulse using the kernel
context.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gt/intel_engine_heartbeat.c  | 14 +++++++++++++
 .../gpu/drm/i915/gt/intel_engine_heartbeat.h  |  1 +
 drivers/gpu/drm/i915/i915_active.c            | 21 +++++++++++++++++--
 3 files changed, 34 insertions(+), 2 deletions(-)

Comments

Tvrtko Ursulin Oct. 11, 2019, 2:56 p.m. UTC | #1
On 10/10/2019 08:14, Chris Wilson wrote:
> If we do find ourselves with an idle barrier inside our active while
> waiting, attempt to flush it by emitting a pulse using the kernel
> context.

The point of this one completely escapes me at the moment. Idle barriers 
are kept in there to be consumed by the engine_pm parking, so if any 
random waiter finds some (there will always be some, as long as the 
engine executed some user context, right?), why would it want to handle 
them? Again just to use the opportunity for some house keeping? But what 
if the system is otherwise quite busy and a low-priority client just 
happens to want to wait on something silly?

Regards,

Tvrtko

> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gt/intel_engine_heartbeat.c  | 14 +++++++++++++
>   .../gpu/drm/i915/gt/intel_engine_heartbeat.h  |  1 +
>   drivers/gpu/drm/i915/i915_active.c            | 21 +++++++++++++++++--
>   3 files changed, 34 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
> index f68acf9118f3..e27bb7f028bd 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
> @@ -169,3 +169,17 @@ int intel_engine_pulse(struct intel_engine_cs *engine)
>   	intel_engine_pm_put(engine);
>   	return err;
>   }
> +
> +int intel_engine_flush_barriers(struct intel_engine_cs *engine)
> +{
> +	struct i915_request *rq;
> +
> +	rq = i915_request_create(engine->kernel_context);
> +	if (IS_ERR(rq))
> +		return PTR_ERR(rq);
> +
> +	idle_pulse(engine, rq);
> +	i915_request_add(rq);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
> index 39391004554d..0c1ad0fc091d 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
> @@ -15,5 +15,6 @@ void intel_engine_park_heartbeat(struct intel_engine_cs *engine);
>   void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine);
>   
>   int intel_engine_pulse(struct intel_engine_cs *engine);
> +int intel_engine_flush_barriers(struct intel_engine_cs *engine);
>   
>   #endif /* INTEL_ENGINE_HEARTBEAT_H */
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index aa37c07004b9..98d5fe1c7e19 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -6,6 +6,7 @@
>   
>   #include <linux/debugobjects.h>
>   
> +#include "gt/intel_engine_heartbeat.h"
>   #include "gt/intel_engine_pm.h"
>   
>   #include "i915_drv.h"
> @@ -435,6 +436,21 @@ static void enable_signaling(struct i915_active_fence *active)
>   	dma_fence_put(fence);
>   }
>   
> +static int flush_barrier(struct active_node *it)
> +{
> +	struct intel_engine_cs *engine;
> +
> +	if (!is_barrier(&it->base))
> +		return 0;
> +
> +	engine = __barrier_to_engine(it);
> +	smp_rmb(); /* serialise with add_active_barriers */
> +	if (!is_barrier(&it->base))
> +		return 0;
> +
> +	return intel_engine_flush_barriers(engine);
> +}
> +
>   int i915_active_wait(struct i915_active *ref)
>   {
>   	struct active_node *it, *n;
> @@ -448,8 +464,9 @@ int i915_active_wait(struct i915_active *ref)
>   	/* Flush lazy signals */
>   	enable_signaling(&ref->excl);
>   	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> -		if (is_barrier(&it->base)) /* unconnected idle barrier */
> -			continue;
> +		err = flush_barrier(it); /* unconnected idle barrier? */
> +		if (err)
> +			break;
>   
>   		enable_signaling(&it->base);
>   	}
>
Chris Wilson Oct. 11, 2019, 3:11 p.m. UTC | #2
Quoting Tvrtko Ursulin (2019-10-11 15:56:35)
> 
> On 10/10/2019 08:14, Chris Wilson wrote:
> > If we do find ourselves with an idle barrier inside our active while
> > waiting, attempt to flush it by emitting a pulse using the kernel
> > context.
> 
> The point of this one completely escapes me at the moment. Idle barriers 
> are kept in there to be consumed by the engine_pm parking, so if any 
> random waiter finds some (there will always be some, as long as the 
> engine executed some user context, right?),

Not any random waiter; the waiter has to be waiting on a context that
was active and so setup a barrier.

> why would it want to handle 
> them? Again just to use the opportunity for some house keeping? But what 
> if the system is otherwise quite busy and a low-priority client just 
> happens to want to wait on something silly?

There's no guarantee that it will ever be flushed. So why wouldn't we
use a low priority request to give a semblance of forward progress and
give a guarantee that the wait will complete.

It's a hypothetical point, there are no waiters that need to wait upon
their own barriers at present. We are just completing the picture for
idle barrier tracking.
-Chris
Tvrtko Ursulin Oct. 14, 2019, 1:08 p.m. UTC | #3
On 11/10/2019 16:11, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-10-11 15:56:35)
>>
>> On 10/10/2019 08:14, Chris Wilson wrote:
>>> If we do find ourselves with an idle barrier inside our active while
>>> waiting, attempt to flush it by emitting a pulse using the kernel
>>> context.
>>
>> The point of this one completely escapes me at the moment. Idle barriers
>> are kept in there to be consumed by the engine_pm parking, so if any
>> random waiter finds some (there will always be some, as long as the
>> engine executed some user context, right?),
> 
> Not any random waiter; the waiter has to be waiting on a context that
> was active and so setup a barrier.
> 
>> why would it want to handle
>> them? Again just to use the opportunity for some house keeping? But what
>> if the system is otherwise quite busy and a low-priority client just
>> happens to want to wait on something silly?
> 
> There's no guarantee that it will ever be flushed. So why wouldn't we
> use a low priority request to give a semblance of forward progress and
> give a guarantee that the wait will complete.
> 
> It's a hypothetical point, there are no waiters that need to wait upon
> their own barriers at present. We are just completing the picture for
> idle barrier tracking.

Hm I was mistakenly remembering things like rpcs reconfiguration would 
wait on ce->active, but I forgot about your trick with putting kernel 
context request on an user timeline.

I guess it is fine there, but since, and as you have said, it is 
hypothetical, then this patch is dead code and can wait.

Regards,

Tvrtko
Chris Wilson Oct. 14, 2019, 1:38 p.m. UTC | #4
Quoting Tvrtko Ursulin (2019-10-14 14:08:12)
> 
> On 11/10/2019 16:11, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2019-10-11 15:56:35)
> >>
> >> On 10/10/2019 08:14, Chris Wilson wrote:
> >>> If we do find ourselves with an idle barrier inside our active while
> >>> waiting, attempt to flush it by emitting a pulse using the kernel
> >>> context.
> >>
> >> The point of this one completely escapes me at the moment. Idle barriers
> >> are kept in there to be consumed by the engine_pm parking, so if any
> >> random waiter finds some (there will always be some, as long as the
> >> engine executed some user context, right?),
> > 
> > Not any random waiter; the waiter has to be waiting on a context that
> > was active and so setup a barrier.
> > 
> >> why would it want to handle
> >> them? Again just to use the opportunity for some house keeping? But what
> >> if the system is otherwise quite busy and a low-priority client just
> >> happens to want to wait on something silly?
> > 
> > There's no guarantee that it will ever be flushed. So why wouldn't we
> > use a low priority request to give a semblance of forward progress and
> > give a guarantee that the wait will complete.
> > 
> > It's a hypothetical point, there are no waiters that need to wait upon
> > their own barriers at present. We are just completing the picture for
> > idle barrier tracking.
> 
> Hm I was mistakenly remembering things like rpcs reconfiguration would 
> wait on ce->active, but I forgot about your trick with putting kernel 
> context request on an user timeline.
> 
> I guess it is fine there, but since, and as you have said, it is 
> hypothetical, then this patch is dead code and can wait.

Why would we even bother checking against the potential invalid pointer
dereference then?... :-p
-Chris
Chris Wilson Oct. 23, 2019, 3:33 p.m. UTC | #5
Quoting Tvrtko Ursulin (2019-10-14 14:08:12)
> 
> On 11/10/2019 16:11, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2019-10-11 15:56:35)
> >>
> >> On 10/10/2019 08:14, Chris Wilson wrote:
> >>> If we do find ourselves with an idle barrier inside our active while
> >>> waiting, attempt to flush it by emitting a pulse using the kernel
> >>> context.
> >>
> >> The point of this one completely escapes me at the moment. Idle barriers
> >> are kept in there to be consumed by the engine_pm parking, so if any
> >> random waiter finds some (there will always be some, as long as the
> >> engine executed some user context, right?),
> > 
> > Not any random waiter; the waiter has to be waiting on a context that
> > was active and so setup a barrier.
> > 
> >> why would it want to handle
> >> them? Again just to use the opportunity for some house keeping? But what
> >> if the system is otherwise quite busy and a low-priority client just
> >> happens to want to wait on something silly?
> > 
> > There's no guarantee that it will ever be flushed. So why wouldn't we
> > use a low priority request to give a semblance of forward progress and
> > give a guarantee that the wait will complete.
> > 
> > It's a hypothetical point, there are no waiters that need to wait upon
> > their own barriers at present. We are just completing the picture for
> > idle barrier tracking.
> 
> Hm I was mistakenly remembering things like rpcs reconfiguration would 
> wait on ce->active, but I forgot about your trick with putting kernel 
> context request on an user timeline.
> 
> I guess it is fine there, but since, and as you have said, it is 
> hypothetical, then this patch is dead code and can wait.

Ok, I have a use for this now! In "drm/i915: Allow userspace to specify
ringsize on construction" we need to wait on the context itself to idle,
i.e. i915_active_wait(&ce->active) and so now it is possible for us to
be waiting on an idle_barrier() and so the flush be beneficial.

static int __apply_ringsize(struct intel_context *ce, void *sz)
{
       int err;

       err = i915_active_wait(&ce->active);
       if (err < 0)
               return err;

       if (intel_context_lock_pinned(ce))
               return -EINTR;

       if (intel_context_is_pinned(ce)) {
               err = -EBUSY; /* In active use, come back later! */
               goto unlock;
       }

       if (test_bit(CONTEXT_ALLOC_BIT, &ce->flags)) {
               struct intel_ring *ring;

               /* Replace the existing ringbuffer */
               ring = intel_engine_create_ring(ce->engine,
                                               (unsigned long)sz);
               if (IS_ERR(ring)) {
                       err = PTR_ERR(ring);
                       goto unlock;
               }

               intel_ring_put(ce->ring);
               ce->ring = ring;

               /* Context image will be updated on next pin */
       } else {
               ce->ring = sz;
       }

unlock:
       intel_context_unlock_pinned(ce);
       return err;
}

-Chris
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
index f68acf9118f3..e27bb7f028bd 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
@@ -169,3 +169,17 @@  int intel_engine_pulse(struct intel_engine_cs *engine)
 	intel_engine_pm_put(engine);
 	return err;
 }
+
+int intel_engine_flush_barriers(struct intel_engine_cs *engine)
+{
+	struct i915_request *rq;
+
+	rq = i915_request_create(engine->kernel_context);
+	if (IS_ERR(rq))
+		return PTR_ERR(rq);
+
+	idle_pulse(engine, rq);
+	i915_request_add(rq);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
index 39391004554d..0c1ad0fc091d 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.h
@@ -15,5 +15,6 @@  void intel_engine_park_heartbeat(struct intel_engine_cs *engine);
 void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine);
 
 int intel_engine_pulse(struct intel_engine_cs *engine);
+int intel_engine_flush_barriers(struct intel_engine_cs *engine);
 
 #endif /* INTEL_ENGINE_HEARTBEAT_H */
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index aa37c07004b9..98d5fe1c7e19 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -6,6 +6,7 @@ 
 
 #include <linux/debugobjects.h>
 
+#include "gt/intel_engine_heartbeat.h"
 #include "gt/intel_engine_pm.h"
 
 #include "i915_drv.h"
@@ -435,6 +436,21 @@  static void enable_signaling(struct i915_active_fence *active)
 	dma_fence_put(fence);
 }
 
+static int flush_barrier(struct active_node *it)
+{
+	struct intel_engine_cs *engine;
+
+	if (!is_barrier(&it->base))
+		return 0;
+
+	engine = __barrier_to_engine(it);
+	smp_rmb(); /* serialise with add_active_barriers */
+	if (!is_barrier(&it->base))
+		return 0;
+
+	return intel_engine_flush_barriers(engine);
+}
+
 int i915_active_wait(struct i915_active *ref)
 {
 	struct active_node *it, *n;
@@ -448,8 +464,9 @@  int i915_active_wait(struct i915_active *ref)
 	/* Flush lazy signals */
 	enable_signaling(&ref->excl);
 	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
-		if (is_barrier(&it->base)) /* unconnected idle barrier */
-			continue;
+		err = flush_barrier(it); /* unconnected idle barrier? */
+		if (err)
+			break;
 
 		enable_signaling(&it->base);
 	}