diff mbox

[v10] drm/i915: Extend LRC pinning to cover GPU context writeback

Message ID 569CFE81.9070006@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tvrtko Ursulin Jan. 18, 2016, 3:02 p.m. UTC
Hi guys,

On 15/01/16 10:59, Nick Hoath wrote:
> On 14/01/2016 12:37, Nick Hoath wrote:
>> On 14/01/2016 12:31, Chris Wilson wrote:
>>> On Thu, Jan 14, 2016 at 11:56:07AM +0000, Nick Hoath wrote:
>>>> On 14/01/2016 11:36, Chris Wilson wrote:
>>>>> On Wed, Jan 13, 2016 at 04:19:45PM +0000, Nick Hoath wrote:
>>>>>> +    if (ctx->engine[ring->id].dirty) {
>>>>>> +        struct drm_i915_gem_request *req = NULL;
>>>>>> +
>>>>>> +        /**
>>>>>> +         * If there is already a request pending on
>>>>>> +         * this ring, wait for that to complete,
>>>>>> +         * otherwise create a switch to idle request
>>>>>> +         */
>>>>>> +        if (list_empty(&ring->request_list)) {
>>>>>> +            int ret;
>>>>>> +
>>>>>> +            ret = i915_gem_request_alloc(
>>>>>> +                    ring,
>>>>>> +                    ring->default_context,
>>>>>> +                    &req);
>>>>>> +            if (!ret)
>>>>>> +                i915_add_request(req);
>>>>>> +            else
>>>>>> +                DRM_DEBUG("Failed to ensure context saved");
>>>>>> +        } else {
>>>>>> +            req = list_first_entry(
>>>>>> +                    &ring->request_list,
>>>>>> +                    typeof(*req), list);
>>>>>> +        }
>>>>>> +        if (req) {
>>>>>> +            ret = i915_wait_request(req);
>>>>>> +            if (ret != 0) {
>>>>>> +                /**
>>>>>> +                 * If we get here, there's probably been a ring
>>>>>> +                 * reset, so we just clean up the dirty flag.&
>>>>>> +                 * pin count.
>>>>>> +                 */
>>>>>> +                ctx->engine[ring->id].dirty = false;
>>>>>> +                __intel_lr_context_unpin(
>>>>>> +                    ring,
>>>>>> +                    ctx);
>>>>>> +            }
>>>>>> +        }
>>>>>
>>>>> If you were to take a lr_context_pin on the last_context, and only
>>>>> release that pin when you change to a new context, you do not need to
>>>>
>>>> That what this patch does.
>>>>
>>>>> introduce a blocking context-close, nor do you need to introduce the
>>>>> usage of default_context.
>>>>
>>>> The use of default_context here is to stop a context hanging around
>>>> after it is no longer needed.
>>>
>>> By blocking, which is not acceptable. Also we can eliminate the
>>> default_context and so pinning that opposed to the last_context serves
>>> no purpose other than by chance having a more preferrable position when
>>> it comes to defragmentation. But you don't enable that anyway and we
>>
>> Enabling the shrinker on execlists is something I'm working on which is
>> predicated on this patch. Also why is blocking on closing a context not
>> acceptable?
>>
> 
> As a clarification: Without rewriting the execlist code to not submit or 
> cleanup from an interrupt handler, we can't use refcounting to allow non 
> blocking closing.

I am trying to understand this issue so please bear with me if I got it
wrong. And also it is not tested since I don't have a suitable system.

But... would something like the below be an interesting step towards an
acceptable solution?

unreferenced if there is a *following* *completed* request.

This way, regardless of whether they are using the same or different
contexts, we can be sure that the GPU has either completed the 
context writing, or that the unreference will not cause the final
unpin of the context.

With the above snippet it would leave the last context pinned, but,
that could also be improved by either appending a default empty
context when we get to the end of the list, or perhaps periodically
from the retire worker only to lessen the ctx switch traffic.

Thoughts, comments?

Regards,

Tvrtko

Comments

Chris Wilson Jan. 18, 2016, 4:53 p.m. UTC | #1
On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
> -       while (!list_empty(&ring->request_list)) {
> -               struct drm_i915_gem_request *request;
> -
> -               request = list_first_entry(&ring->request_list,
> -                                          struct drm_i915_gem_request,
> -                                          list);
> -
> -               if (!i915_gem_request_completed(request, true))
> +       list_for_each_entry_safe(req, next, &ring->request_list, list) {
> +               if (!i915_gem_request_completed(req, true))
>                         break;
>  
> -               i915_gem_request_retire(request);
> +               if (!i915.enable_execlists || !i915.enable_guc_submission) {
> +                       i915_gem_request_retire(req);
> +               } else {
> +                       prev_req = list_prev_entry(req, list);
> +                       if (prev_req)
> +                               i915_gem_request_retire(prev_req);
> +               }
>         }
> 
> To explain, this attempts to ensure that in GuC mode requests are only
> unreferenced if there is a *following* *completed* request.
> 
> This way, regardless of whether they are using the same or different
> contexts, we can be sure that the GPU has either completed the 
> context writing, or that the unreference will not cause the final
> unpin of the context.

This is the first bogus step. contexts have to be unreferenced from
request retire, not request free. As it stands today, this forces us to
hold the struct_mutex for the free (causing many foul ups along the
line).  The only reason why it is like that is because of execlists not
decoupling its context pinning inside request cancel.
-Chris
Tvrtko Ursulin Jan. 18, 2016, 5:14 p.m. UTC | #2
On 18/01/16 16:53, Chris Wilson wrote:
> On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
>> -       while (!list_empty(&ring->request_list)) {
>> -               struct drm_i915_gem_request *request;
>> -
>> -               request = list_first_entry(&ring->request_list,
>> -                                          struct drm_i915_gem_request,
>> -                                          list);
>> -
>> -               if (!i915_gem_request_completed(request, true))
>> +       list_for_each_entry_safe(req, next, &ring->request_list, list) {
>> +               if (!i915_gem_request_completed(req, true))
>>                          break;
>>
>> -               i915_gem_request_retire(request);
>> +               if (!i915.enable_execlists || !i915.enable_guc_submission) {
>> +                       i915_gem_request_retire(req);
>> +               } else {
>> +                       prev_req = list_prev_entry(req, list);
>> +                       if (prev_req)
>> +                               i915_gem_request_retire(prev_req);
>> +               }
>>          }
>>
>> To explain, this attempts to ensure that in GuC mode requests are only
>> unreferenced if there is a *following* *completed* request.
>>
>> This way, regardless of whether they are using the same or different
>> contexts, we can be sure that the GPU has either completed the
>> context writing, or that the unreference will not cause the final
>> unpin of the context.
>
> This is the first bogus step. contexts have to be unreferenced from
> request retire, not request free. As it stands today, this forces us to
> hold the struct_mutex for the free (causing many foul ups along the
> line).  The only reason why it is like that is because of execlists not
> decoupling its context pinning inside request cancel.

What is the first bogus step? My idea of how to fix the GuC issue, or 
the mention of final unreference in relation to GPU completing the 
submission?

Also I don't understand how would you decouple context and request lifetime?

Maybe we can ignore execlist mode for the moment and just consider the 
GuC which, as much as I understand it, has a simpler and fully aligned 
request/context/lrc lifetime of:

* reference and pin and request creation
* unpin and unreference at retire

Where retire is decoupled from actual GPU activity, or maybe better say 
indirectly driven.

Execlists bolt on a parallel another instance reference and pin on top, 
with different lifetime rules so maybe ignore that for the GuC 
discussion. Just to figure out what you have in mind.

Regards,

Tvrtko
Chris Wilson Jan. 18, 2016, 8:47 p.m. UTC | #3
On Mon, Jan 18, 2016 at 05:14:26PM +0000, Tvrtko Ursulin wrote:
> 
> On 18/01/16 16:53, Chris Wilson wrote:
> >On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
> >>-       while (!list_empty(&ring->request_list)) {
> >>-               struct drm_i915_gem_request *request;
> >>-
> >>-               request = list_first_entry(&ring->request_list,
> >>-                                          struct drm_i915_gem_request,
> >>-                                          list);
> >>-
> >>-               if (!i915_gem_request_completed(request, true))
> >>+       list_for_each_entry_safe(req, next, &ring->request_list, list) {
> >>+               if (!i915_gem_request_completed(req, true))
> >>                         break;
> >>
> >>-               i915_gem_request_retire(request);
> >>+               if (!i915.enable_execlists || !i915.enable_guc_submission) {
> >>+                       i915_gem_request_retire(req);
> >>+               } else {
> >>+                       prev_req = list_prev_entry(req, list);
> >>+                       if (prev_req)
> >>+                               i915_gem_request_retire(prev_req);
> >>+               }
> >>         }
> >>
> >>To explain, this attempts to ensure that in GuC mode requests are only
> >>unreferenced if there is a *following* *completed* request.
> >>
> >>This way, regardless of whether they are using the same or different
> >>contexts, we can be sure that the GPU has either completed the
> >>context writing, or that the unreference will not cause the final
> >>unpin of the context.
> >
> >This is the first bogus step. contexts have to be unreferenced from
> >request retire, not request free. As it stands today, this forces us to
> >hold the struct_mutex for the free (causing many foul ups along the
> >line).  The only reason why it is like that is because of execlists not
> >decoupling its context pinning inside request cancel.
> 
> What is the first bogus step? My idea of how to fix the GuC issue,
> or the mention of final unreference in relation to GPU completing
> the submission?

That we want to want to actually unreference the request. We want to
unpin the context at the appropriate juncture. At the moment, it looks
like that you are conflating those two steps: "requests are only
unreferenced". Using the retirement mechanism would mean coupling the
context unpinning into a subsequent request rather than defer retiring a
completed request, for example legacy uses active vma tracking to
accomplish the same thing. Aiui, the current claim is that we couldn't
do that since the guc may reorder contexts - except that we currently
use a global seqno so that would be bad on many levels.
-Chris
Tvrtko Ursulin Jan. 19, 2016, 10:24 a.m. UTC | #4
On 18/01/16 20:47, Chris Wilson wrote:
> On Mon, Jan 18, 2016 at 05:14:26PM +0000, Tvrtko Ursulin wrote:
>>
>> On 18/01/16 16:53, Chris Wilson wrote:
>>> On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
>>>> -       while (!list_empty(&ring->request_list)) {
>>>> -               struct drm_i915_gem_request *request;
>>>> -
>>>> -               request = list_first_entry(&ring->request_list,
>>>> -                                          struct drm_i915_gem_request,
>>>> -                                          list);
>>>> -
>>>> -               if (!i915_gem_request_completed(request, true))
>>>> +       list_for_each_entry_safe(req, next, &ring->request_list, list) {
>>>> +               if (!i915_gem_request_completed(req, true))
>>>>                          break;
>>>>
>>>> -               i915_gem_request_retire(request);
>>>> +               if (!i915.enable_execlists || !i915.enable_guc_submission) {
>>>> +                       i915_gem_request_retire(req);
>>>> +               } else {
>>>> +                       prev_req = list_prev_entry(req, list);
>>>> +                       if (prev_req)
>>>> +                               i915_gem_request_retire(prev_req);
>>>> +               }
>>>>          }
>>>>
>>>> To explain, this attempts to ensure that in GuC mode requests are only
>>>> unreferenced if there is a *following* *completed* request.
>>>>
>>>> This way, regardless of whether they are using the same or different
>>>> contexts, we can be sure that the GPU has either completed the
>>>> context writing, or that the unreference will not cause the final
>>>> unpin of the context.
>>>
>>> This is the first bogus step. contexts have to be unreferenced from
>>> request retire, not request free. As it stands today, this forces us to
>>> hold the struct_mutex for the free (causing many foul ups along the
>>> line).  The only reason why it is like that is because of execlists not
>>> decoupling its context pinning inside request cancel.
>>
>> What is the first bogus step? My idea of how to fix the GuC issue,
>> or the mention of final unreference in relation to GPU completing
>> the submission?
>
> That we want to want to actually unreference the request. We want to
> unpin the context at the appropriate juncture. At the moment, it looks

What would be the appropriate juncture? With GuC we don't have the 
equivalent of context complete irq.

> like that you are conflating those two steps: "requests are only
> unreferenced". Using the retirement mechanism would mean coupling the
> context unpinning into a subsequent request rather than defer retiring a
> completed request, for example legacy uses active vma tracking to
> accomplish the same thing. Aiui, the current claim is that we couldn't
> do that since the guc may reorder contexts - except that we currently
> use a global seqno so that would be bad on many levels.

I don't know legacy. :( I can see that request/context lifetime is 
coupled there and associated with request creation to retirement.

Does it have the same problem of seqno signaling completion before the 
GPU is done with writing out the context image and how does it solve that?

Regards,

Tvrtko
Tvrtko Ursulin Jan. 19, 2016, 5:18 p.m. UTC | #5
On 19/01/16 10:24, Tvrtko Ursulin wrote:
>
>
> On 18/01/16 20:47, Chris Wilson wrote:
>> On Mon, Jan 18, 2016 at 05:14:26PM +0000, Tvrtko Ursulin wrote:
>>>
>>> On 18/01/16 16:53, Chris Wilson wrote:
>>>> On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
>>>>> -       while (!list_empty(&ring->request_list)) {
>>>>> -               struct drm_i915_gem_request *request;
>>>>> -
>>>>> -               request = list_first_entry(&ring->request_list,
>>>>> -                                          struct
>>>>> drm_i915_gem_request,
>>>>> -                                          list);
>>>>> -
>>>>> -               if (!i915_gem_request_completed(request, true))
>>>>> +       list_for_each_entry_safe(req, next, &ring->request_list,
>>>>> list) {
>>>>> +               if (!i915_gem_request_completed(req, true))
>>>>>                          break;
>>>>>
>>>>> -               i915_gem_request_retire(request);
>>>>> +               if (!i915.enable_execlists ||
>>>>> !i915.enable_guc_submission) {
>>>>> +                       i915_gem_request_retire(req);
>>>>> +               } else {
>>>>> +                       prev_req = list_prev_entry(req, list);
>>>>> +                       if (prev_req)
>>>>> +                               i915_gem_request_retire(prev_req);
>>>>> +               }
>>>>>          }
>>>>>
>>>>> To explain, this attempts to ensure that in GuC mode requests are only
>>>>> unreferenced if there is a *following* *completed* request.
>>>>>
>>>>> This way, regardless of whether they are using the same or different
>>>>> contexts, we can be sure that the GPU has either completed the
>>>>> context writing, or that the unreference will not cause the final
>>>>> unpin of the context.
>>>>
>>>> This is the first bogus step. contexts have to be unreferenced from
>>>> request retire, not request free. As it stands today, this forces us to
>>>> hold the struct_mutex for the free (causing many foul ups along the
>>>> line).  The only reason why it is like that is because of execlists not
>>>> decoupling its context pinning inside request cancel.
>>>
>>> What is the first bogus step? My idea of how to fix the GuC issue,
>>> or the mention of final unreference in relation to GPU completing
>>> the submission?
>>
>> That we want to want to actually unreference the request. We want to
>> unpin the context at the appropriate juncture. At the moment, it looks
>
> What would be the appropriate juncture? With GuC we don't have the
> equivalent of context complete irq.
>
>> like that you are conflating those two steps: "requests are only
>> unreferenced". Using the retirement mechanism would mean coupling the
>> context unpinning into a subsequent request rather than defer retiring a
>> completed request, for example legacy uses active vma tracking to
>> accomplish the same thing. Aiui, the current claim is that we couldn't
>> do that since the guc may reorder contexts - except that we currently
>> use a global seqno so that would be bad on many levels.
>
> I don't know legacy. :( I can see that request/context lifetime is
> coupled there and associated with request creation to retirement.
>
> Does it have the same problem of seqno signaling completion before the
> GPU is done with writing out the context image and how does it solve that?

Ok I think I am starting to see the legacy code paths.

Interesting areas are i915_switch_context + do_switch which do the 
ring->last_context tracking and make the ring/engine own one extra 
reference on the context.

Then, code paths which want to make sure no user context are active on 
the GPU call i915_gpu_idle and submit a dummy default context request.

The latter even explicitly avoids execlist mode.

So unless I am missing something, we could just unify the behaviour 
between the two. Make ring/engine->last_context do the identical 
tracking as legacy context switching and let i915_gpu_idle idle the GPU 
in execlist mode as well?

Regards,

Tvrtko
Tvrtko Ursulin Jan. 19, 2016, 5:31 p.m. UTC | #6
On 19/01/16 17:18, Tvrtko Ursulin wrote:
>
> On 19/01/16 10:24, Tvrtko Ursulin wrote:
>>
>>
>> On 18/01/16 20:47, Chris Wilson wrote:
>>> On Mon, Jan 18, 2016 at 05:14:26PM +0000, Tvrtko Ursulin wrote:
>>>>
>>>> On 18/01/16 16:53, Chris Wilson wrote:
>>>>> On Mon, Jan 18, 2016 at 03:02:25PM +0000, Tvrtko Ursulin wrote:
>>>>>> -       while (!list_empty(&ring->request_list)) {
>>>>>> -               struct drm_i915_gem_request *request;
>>>>>> -
>>>>>> -               request = list_first_entry(&ring->request_list,
>>>>>> -                                          struct
>>>>>> drm_i915_gem_request,
>>>>>> -                                          list);
>>>>>> -
>>>>>> -               if (!i915_gem_request_completed(request, true))
>>>>>> +       list_for_each_entry_safe(req, next, &ring->request_list,
>>>>>> list) {
>>>>>> +               if (!i915_gem_request_completed(req, true))
>>>>>>                          break;
>>>>>>
>>>>>> -               i915_gem_request_retire(request);
>>>>>> +               if (!i915.enable_execlists ||
>>>>>> !i915.enable_guc_submission) {
>>>>>> +                       i915_gem_request_retire(req);
>>>>>> +               } else {
>>>>>> +                       prev_req = list_prev_entry(req, list);
>>>>>> +                       if (prev_req)
>>>>>> +                               i915_gem_request_retire(prev_req);
>>>>>> +               }
>>>>>>          }
>>>>>>
>>>>>> To explain, this attempts to ensure that in GuC mode requests are
>>>>>> only
>>>>>> unreferenced if there is a *following* *completed* request.
>>>>>>
>>>>>> This way, regardless of whether they are using the same or different
>>>>>> contexts, we can be sure that the GPU has either completed the
>>>>>> context writing, or that the unreference will not cause the final
>>>>>> unpin of the context.
>>>>>
>>>>> This is the first bogus step. contexts have to be unreferenced from
>>>>> request retire, not request free. As it stands today, this forces
>>>>> us to
>>>>> hold the struct_mutex for the free (causing many foul ups along the
>>>>> line).  The only reason why it is like that is because of execlists
>>>>> not
>>>>> decoupling its context pinning inside request cancel.
>>>>
>>>> What is the first bogus step? My idea of how to fix the GuC issue,
>>>> or the mention of final unreference in relation to GPU completing
>>>> the submission?
>>>
>>> That we want to want to actually unreference the request. We want to
>>> unpin the context at the appropriate juncture. At the moment, it looks
>>
>> What would be the appropriate juncture? With GuC we don't have the
>> equivalent of context complete irq.
>>
>>> like that you are conflating those two steps: "requests are only
>>> unreferenced". Using the retirement mechanism would mean coupling the
>>> context unpinning into a subsequent request rather than defer retiring a
>>> completed request, for example legacy uses active vma tracking to
>>> accomplish the same thing. Aiui, the current claim is that we couldn't
>>> do that since the guc may reorder contexts - except that we currently
>>> use a global seqno so that would be bad on many levels.
>>
>> I don't know legacy. :( I can see that request/context lifetime is
>> coupled there and associated with request creation to retirement.
>>
>> Does it have the same problem of seqno signaling completion before the
>> GPU is done with writing out the context image and how does it solve
>> that?
>
> Ok I think I am starting to see the legacy code paths.
>
> Interesting areas are i915_switch_context + do_switch which do the
> ring->last_context tracking and make the ring/engine own one extra
> reference on the context.
>
> Then, code paths which want to make sure no user context are active on
> the GPU call i915_gpu_idle and submit a dummy default context request.
>
> The latter even explicitly avoids execlist mode.
>
> So unless I am missing something, we could just unify the behaviour
> between the two. Make ring/engine->last_context do the identical
> tracking as legacy context switching and let i915_gpu_idle idle the GPU
> in execlist mode as well?

Although I am not sure the engine->last_context concept works with LRC 
and GuC because of the multiple submission ports. Need to give it more 
thought.

Regards,

Tvrtko
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 2cfcf9401971..63bb251edffd 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2927,6 +2927,8 @@  void i915_gem_reset(struct drm_device *dev)
 void
 i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
 {
+       struct drm_i915_gem_request *prev_req, *next, *req;
+
        WARN_ON(i915_verify_lists(ring->dev));
 
        /* Retire requests first as we use it above for the early return.
@@ -2934,17 +2936,17 @@  i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
         * the requests lists without clearing the active list, leading to
         * confusion.
         */
-       while (!list_empty(&ring->request_list)) {
-               struct drm_i915_gem_request *request;
-
-               request = list_first_entry(&ring->request_list,
-                                          struct drm_i915_gem_request,
-                                          list);
-
-               if (!i915_gem_request_completed(request, true))
+       list_for_each_entry_safe(req, next, &ring->request_list, list) {
+               if (!i915_gem_request_completed(req, true))
                        break;
 
-               i915_gem_request_retire(request);
+               if (!i915.enable_execlists || !i915.enable_guc_submission) {
+                       i915_gem_request_retire(req);
+               } else {
+                       prev_req = list_prev_entry(req, list);
+                       if (prev_req)
+                               i915_gem_request_retire(prev_req);
+               }
        }

To explain, this attempts to ensure that in GuC mode requests are only