From: Michel Thierry <michel.thie...@intel.com>

Context switch (and execlist submission) should happen only when
other contexts are not active, otherwise pre-emption occurs.

To assure this, we place context switch requests in a queue and those
request are later consumed when the right context switch interrupt is
received.

Signed-off-by: Michel Thierry <michel.thie...@intel.com>

v2: Use a spinlock, do not remove the requests on unqueue (wait for
context switch completion).

Signed-off-by: Thomas Daniel <thomas.dan...@intel.com>

v3: Several rebases and code changes. Use unique ID.

Signed-off-by: Oscar Mateo <oscar.ma...@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h         |  6 ++++
 drivers/gpu/drm/i915/i915_lrc.c         | 56 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_ringbuffer.c |  8 +++++
 drivers/gpu/drm/i915/intel_ringbuffer.h |  4 +++
 4 files changed, 74 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b28b785..2607664 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1676,6 +1676,9 @@ struct drm_i915_gem_request {
        struct drm_i915_file_private *file_priv;
        /** file_priv list entry for this request */
        struct list_head client_list;
+
+       /** execlist queue entry for this request */
+       struct list_head execlist_link;
 };
 
 struct drm_i915_file_private {
@@ -2338,6 +2341,9 @@ struct i915_hw_context *gen8_gem_create_context(struct 
drm_device *dev,
                        struct drm_i915_file_private *file_priv,
                        struct i915_hw_context *standalone_ctx, bool create_vm);
 void gen8_gem_context_free(struct i915_hw_context *ctx);
+int gen8_switch_context_queue(struct intel_engine *ring,
+                             struct i915_hw_context *to,
+                             u32 tail);
 
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/i915_lrc.c b/drivers/gpu/drm/i915/i915_lrc.c
index e564bac..4cacabb 100644
--- a/drivers/gpu/drm/i915/i915_lrc.c
+++ b/drivers/gpu/drm/i915/i915_lrc.c
@@ -233,6 +233,62 @@ static int gen8_switch_context(struct intel_engine *ring,
        return 0;
 }
 
+static void gen8_switch_context_unqueue(struct intel_engine *ring)
+{
+       struct drm_i915_gem_request *req0 = NULL, *req1 = NULL;
+       struct drm_i915_gem_request *cursor = NULL, *tmp = NULL;
+
+       if (list_empty(&ring->execlist_queue))
+               return;
+
+       /* Try to read in pairs */
+       list_for_each_entry_safe(cursor, tmp, &ring->execlist_queue, 
execlist_link) {
+               if (!req0)
+                       req0 = cursor;
+               else if (get_submission_id(req0->ctx) == 
get_submission_id(cursor->ctx)) {
+                       /* Same ID: ignore first request, as second request
+                        * will update tail past first request's workload */
+                       list_del(&req0->execlist_link);
+                       i915_gem_context_unreference(req0->ctx);
+                       kfree(req0);
+                       req0 = cursor;
+               } else {
+                       req1 = cursor;
+                       break;
+               }
+       }
+
+       BUG_ON(gen8_switch_context(ring, req0->ctx, req0->tail,
+                       req1? req1->ctx : NULL, req1? req1->tail : 0));
+}
+
+int gen8_switch_context_queue(struct intel_engine *ring,
+                             struct i915_hw_context *to,
+                             u32 tail)
+{
+       struct drm_i915_gem_request *req = NULL;
+       unsigned long flags;
+       bool was_empty;
+
+       req = (struct drm_i915_gem_request *)
+               kmalloc(sizeof(struct drm_i915_gem_request), GFP_KERNEL);
+       req->ring = ring;
+       req->ctx = to;
+       i915_gem_context_reference(req->ctx);
+       req->tail = tail;
+
+       spin_lock_irqsave(&ring->execlist_lock, flags);
+
+       was_empty = list_empty(&ring->execlist_queue);
+       list_add_tail(&req->execlist_link, &ring->execlist_queue);
+       if (was_empty)
+               gen8_switch_context_unqueue(ring);
+
+       spin_unlock_irqrestore(&ring->execlist_lock, flags);
+
+       return 0;
+}
+
 void gen8_gem_context_free(struct i915_hw_context *ctx)
 {
        /* Global default contexts ringbuffers are take care of
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c 
b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 230740e..a92bede 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -2387,6 +2387,8 @@ void intel_init_rings_early(struct drm_device *dev)
        dev_priv->ring[RCS].dev = dev;
        dev_priv->ring[RCS].default_ringbuf.head = 0;
        dev_priv->ring[RCS].default_ringbuf.tail = 0;
+       INIT_LIST_HEAD(&dev_priv->ring[RCS].execlist_queue);
+       spin_lock_init(&dev_priv->ring[RCS].execlist_lock);
 
        dev_priv->ring[BCS].name = "blitter ring";
        dev_priv->ring[BCS].id = BCS;
@@ -2394,6 +2396,8 @@ void intel_init_rings_early(struct drm_device *dev)
        dev_priv->ring[BCS].dev = dev;
        dev_priv->ring[BCS].default_ringbuf.head = 0;
        dev_priv->ring[BCS].default_ringbuf.tail = 0;
+       INIT_LIST_HEAD(&dev_priv->ring[BCS].execlist_queue);
+       spin_lock_init(&dev_priv->ring[BCS].execlist_lock);
 
        dev_priv->ring[VCS].name = "bsd ring";
        dev_priv->ring[VCS].id = VCS;
@@ -2404,6 +2408,8 @@ void intel_init_rings_early(struct drm_device *dev)
        dev_priv->ring[VCS].dev = dev;
        dev_priv->ring[VCS].default_ringbuf.head = 0;
        dev_priv->ring[VCS].default_ringbuf.tail = 0;
+       INIT_LIST_HEAD(&dev_priv->ring[VCS].execlist_queue);
+       spin_lock_init(&dev_priv->ring[VCS].execlist_lock);
 
        dev_priv->ring[VECS].name = "video enhancement ring";
        dev_priv->ring[VECS].id = VECS;
@@ -2411,4 +2417,6 @@ void intel_init_rings_early(struct drm_device *dev)
        dev_priv->ring[VECS].dev = dev;
        dev_priv->ring[VECS].default_ringbuf.head = 0;
        dev_priv->ring[VECS].default_ringbuf.tail = 0;
+       INIT_LIST_HEAD(&dev_priv->ring[VECS].execlist_queue);
+       spin_lock_init(&dev_priv->ring[VECS].execlist_lock);
 }
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h 
b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 9fbb2d5..5f4fd3c 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -167,6 +167,10 @@ struct intel_engine {
         * Do an explicit TLB flush before MI_SET_CONTEXT
         */
        bool itlb_before_ctx_switch;
+
+       spinlock_t execlist_lock;
+       struct list_head execlist_queue;
+
        struct i915_hw_context *default_context;
        struct i915_hw_context *last_context;
 
-- 
1.9.0

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

Reply via email to