From: Michel Thierry <michel.thierry@xxxxxxxxx> 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.thierry@xxxxxxxxx> v2: Use a spinlock, do not remove the requests on unqueue (wait for context switch completion). Signed-off-by: Thomas Daniel <thomas.daniel@xxxxxxxxx> v3: Several rebases and code changes. Use unique ID. Signed-off-by: Oscar Mateo <oscar.mateo@xxxxxxxxx> --- 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@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx