drm/i915/guc: Insert fence on context when deregistering
authorMatthew Brost <matthew.brost@intel.com>
Wed, 21 Jul 2021 21:50:50 +0000 (14:50 -0700)
committerJohn Harrison <John.C.Harrison@Intel.com>
Thu, 22 Jul 2021 17:07:11 +0000 (10:07 -0700)
Sometimes during context pinning a context with the same guc_id is
registered with the GuC. In this a case deregister must be done before
the context can be registered. A fence is inserted on all requests while
the deregister is in flight. Once the G2H is received indicating the
deregistration is complete the context is registered and the fence is
released.

v2:
 (John H)
  - Fix commit message

Cc: John Harrison <john.c.harrison@intel.com>
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: John Harrison <john.c.harrison@intel.com>
Signed-off-by: John Harrison <John.C.Harrison@Intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210721215101.139794-8-matthew.brost@intel.com
drivers/gpu/drm/i915/gt/intel_context.c
drivers/gpu/drm/i915/gt/intel_context_types.h
drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
drivers/gpu/drm/i915/i915_request.h

index 090c13287d3e9665e2409232bf6ad2be2413f0c4..7c9159517f8a618bbf7b0f2f16b7431652b57c6b 100644 (file)
@@ -385,6 +385,7 @@ intel_context_init(struct intel_context *ce, struct intel_engine_cs *engine)
        mutex_init(&ce->pin_mutex);
 
        spin_lock_init(&ce->guc_state.lock);
+       INIT_LIST_HEAD(&ce->guc_state.fences);
 
        ce->guc_id = GUC_INVALID_LRC_ID;
        INIT_LIST_HEAD(&ce->guc_id_link);
index 606c480aec26421efb5df7ca30088ee42738f81b..e0e3a937f7093681e335e3995c2c73b2d133fb8f 100644 (file)
@@ -147,6 +147,11 @@ struct intel_context {
                 * submission
                 */
                u8 sched_state;
+               /*
+                * fences: maintains of list of requests that have a submit
+                * fence related to GuC submission
+                */
+               struct list_head fences;
        } guc_state;
 
        /* GuC scheduling state flags that do not require a lock. */
index 463613a414d2539523bccabc9903aec5a9a4baaa..a0871b800153dde8e883f9987eed5846897ba20a 100644 (file)
@@ -935,6 +935,30 @@ static const struct intel_context_ops guc_context_ops = {
        .destroy = guc_context_destroy,
 };
 
+static void __guc_signal_context_fence(struct intel_context *ce)
+{
+       struct i915_request *rq;
+
+       lockdep_assert_held(&ce->guc_state.lock);
+
+       list_for_each_entry(rq, &ce->guc_state.fences, guc_fence_link)
+               i915_sw_fence_complete(&rq->submit);
+
+       INIT_LIST_HEAD(&ce->guc_state.fences);
+}
+
+static void guc_signal_context_fence(struct intel_context *ce)
+{
+       unsigned long flags;
+
+       GEM_BUG_ON(!context_wait_for_deregister_to_register(ce));
+
+       spin_lock_irqsave(&ce->guc_state.lock, flags);
+       clr_context_wait_for_deregister_to_register(ce);
+       __guc_signal_context_fence(ce);
+       spin_unlock_irqrestore(&ce->guc_state.lock, flags);
+}
+
 static bool context_needs_register(struct intel_context *ce, bool new_guc_id)
 {
        return new_guc_id || test_bit(CONTEXT_LRCA_DIRTY, &ce->flags) ||
@@ -945,6 +969,7 @@ static int guc_request_alloc(struct i915_request *rq)
 {
        struct intel_context *ce = rq->context;
        struct intel_guc *guc = ce_to_guc(ce);
+       unsigned long flags;
        int ret;
 
        GEM_BUG_ON(!intel_context_is_pinned(rq->context));
@@ -989,7 +1014,7 @@ static int guc_request_alloc(struct i915_request *rq)
         * increment (in pin_guc_id) is needed to seal a race with unpin_guc_id.
         */
        if (atomic_add_unless(&ce->guc_id_ref, 1, 0))
-               return 0;
+               goto out;
 
        ret = pin_guc_id(guc, ce);      /* returns 1 if new guc_id assigned */
        if (unlikely(ret < 0))
@@ -1005,6 +1030,28 @@ static int guc_request_alloc(struct i915_request *rq)
 
        clear_bit(CONTEXT_LRCA_DIRTY, &ce->flags);
 
+out:
+       /*
+        * We block all requests on this context if a G2H is pending for a
+        * context deregistration as the GuC will fail a context registration
+        * while this G2H is pending. Once a G2H returns, the fence is released
+        * that is blocking these requests (see guc_signal_context_fence).
+        *
+        * We can safely check the below field outside of the lock as it isn't
+        * possible for this field to transition from being clear to set but
+        * converse is possible, hence the need for the check within the lock.
+        */
+       if (likely(!context_wait_for_deregister_to_register(ce)))
+               return 0;
+
+       spin_lock_irqsave(&ce->guc_state.lock, flags);
+       if (context_wait_for_deregister_to_register(ce)) {
+               i915_sw_fence_await(&rq->submit);
+
+               list_add_tail(&rq->guc_fence_link, &ce->guc_state.fences);
+       }
+       spin_unlock_irqrestore(&ce->guc_state.lock, flags);
+
        return 0;
 }
 
@@ -1301,7 +1348,7 @@ int intel_guc_deregister_done_process_msg(struct intel_guc *guc,
                 */
                with_intel_runtime_pm(runtime_pm, wakeref)
                        register_context(ce);
-               clr_context_wait_for_deregister_to_register(ce);
+               guc_signal_context_fence(ce);
                intel_context_put(ce);
        } else if (context_destroyed(ce)) {
                /* Context has been destroyed */
index 5deb65ec5fa52a2bcaea4fa90f135d8d0ed843d3..717e5b29204675f66d27e34558b2d76ae2b6009b 100644 (file)
@@ -285,6 +285,14 @@ struct i915_request {
                struct hrtimer timer;
        } watchdog;
 
+       /*
+        * Requests may need to be stalled when using GuC submission waiting for
+        * certain GuC operations to complete. If that is the case, stalled
+        * requests are added to a per context list of stalled requests. The
+        * below list_head is the link in that list.
+        */
+       struct list_head guc_fence_link;
+
        I915_SELFTEST_DECLARE(struct {
                struct list_head link;
                unsigned long delay;