aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Widawsky <ben@bwidawsk.net>2012-06-04 17:42:46 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-06-14 11:36:17 -0400
commite055684168af48ac7deb27d7267046a0fb9ef80e (patch)
treebb315de36705c7dd80e1e291267cbc41c1fe845f
parent40521054fd46f94e0368cead312d56e9e442aaab (diff)
drm/i915: context switch implementation
Implement the context switch code as well as the interfaces to do the context switch. This patch also doesn't match 1:1 with the RFC patches. The main difference is that from Daniel's responses the last context object is now stored instead of the last context. This aids in allows us to free the context data structure, and context object independently. There is room for optimization: this code will pin the context object until the next context is active. The optimal way to do it is to actually pin the object, move it to the active list, do the context switch, and then unpin it. This allows the eviction code to actually evict the context object if needed. The context switch code is missing workarounds, they will be implemented in future patches. v2: actually do obj->dirty=1 in switch (daniel) Modified comment around above Remove flags to context switch (daniel) Move mi_set_context code to i915_gem_context.c (daniel) Remove seqno , use lazy request instead (daniel) v3: use i915_gem_request_next_seqno instead of outstanding_lazy_request (Daniel) remove id's from trace events (Daniel) Put the context BO in the instruction domain (Daniel) Don't unref the BO is context switch fails (Chris) Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h3
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c146
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h1
3 files changed, 149 insertions, 1 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 250eeae7c262..7fb0364d5d61 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -313,6 +313,7 @@ struct i915_hw_ppgtt {
313#define DEFAULT_CONTEXT_ID 0 313#define DEFAULT_CONTEXT_ID 0
314struct i915_hw_context { 314struct i915_hw_context {
315 int id; 315 int id;
316 bool is_initialized;
316 struct drm_i915_file_private *file_priv; 317 struct drm_i915_file_private *file_priv;
317 struct intel_ring_buffer *ring; 318 struct intel_ring_buffer *ring;
318 struct drm_i915_gem_object *obj; 319 struct drm_i915_gem_object *obj;
@@ -1382,6 +1383,8 @@ void i915_gem_context_init(struct drm_device *dev);
1382void i915_gem_context_fini(struct drm_device *dev); 1383void i915_gem_context_fini(struct drm_device *dev);
1383void i915_gem_context_open(struct drm_device *dev, struct drm_file *file); 1384void i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
1384void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); 1385void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
1386int i915_switch_context(struct intel_ring_buffer *ring,
1387 struct drm_file *file, int to_id);
1385 1388
1386/* i915_gem_gtt.c */ 1389/* i915_gem_gtt.c */
1387int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev); 1390int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 2aca00235ce3..5248c00707f6 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -190,6 +190,11 @@ err_out:
190 return ret; 190 return ret;
191} 191}
192 192
193static inline bool is_default_context(struct i915_hw_context *ctx)
194{
195 return (ctx == ctx->ring->default_context);
196}
197
193/** 198/**
194 * The default context needs to exist per ring that uses contexts. It stores the 199 * The default context needs to exist per ring that uses contexts. It stores the
195 * context state of the GPU for applications that don't utilize HW contexts, as 200 * context state of the GPU for applications that don't utilize HW contexts, as
@@ -306,8 +311,147 @@ void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
306 mutex_unlock(&dev->struct_mutex); 311 mutex_unlock(&dev->struct_mutex);
307} 312}
308 313
309static __used struct i915_hw_context * 314static struct i915_hw_context *
310i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id) 315i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
311{ 316{
312 return (struct i915_hw_context *)idr_find(&file_priv->context_idr, id); 317 return (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
313} 318}
319
320static inline int
321mi_set_context(struct intel_ring_buffer *ring,
322 struct i915_hw_context *new_context,
323 u32 hw_flags)
324{
325 int ret;
326
327 ret = intel_ring_begin(ring, 4);
328 if (ret)
329 return ret;
330
331 intel_ring_emit(ring, MI_NOOP);
332 intel_ring_emit(ring, MI_SET_CONTEXT);
333 intel_ring_emit(ring, new_context->obj->gtt_offset |
334 MI_MM_SPACE_GTT |
335 MI_SAVE_EXT_STATE_EN |
336 MI_RESTORE_EXT_STATE_EN |
337 hw_flags);
338 /* w/a: MI_SET_CONTEXT must always be followed by MI_NOOP */
339 intel_ring_emit(ring, MI_NOOP);
340
341 intel_ring_advance(ring);
342
343 return ret;
344}
345
346static int do_switch(struct drm_i915_gem_object *from_obj,
347 struct i915_hw_context *to,
348 u32 seqno)
349{
350 struct intel_ring_buffer *ring = NULL;
351 u32 hw_flags = 0;
352 int ret;
353
354 BUG_ON(to == NULL);
355 BUG_ON(from_obj != NULL && from_obj->pin_count == 0);
356
357 ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false);
358 if (ret)
359 return ret;
360
361 if (!to->is_initialized || is_default_context(to))
362 hw_flags |= MI_RESTORE_INHIBIT;
363 else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */
364 hw_flags |= MI_FORCE_RESTORE;
365
366 ring = to->ring;
367 ret = mi_set_context(ring, to, hw_flags);
368 if (ret) {
369 i915_gem_object_unpin(to->obj);
370 return ret;
371 }
372
373 /* The backing object for the context is done after switching to the
374 * *next* context. Therefore we cannot retire the previous context until
375 * the next context has already started running. In fact, the below code
376 * is a bit suboptimal because the retiring can occur simply after the
377 * MI_SET_CONTEXT instead of when the next seqno has completed.
378 */
379 if (from_obj != NULL) {
380 from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
381 i915_gem_object_move_to_active(from_obj, ring, seqno);
382 /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
383 * whole damn pipeline, we don't need to explicitly mark the
384 * object dirty. The only exception is that the context must be
385 * correct in case the object gets swapped out. Ideally we'd be
386 * able to defer doing this until we know the object would be
387 * swapped, but there is no way to do that yet.
388 */
389 from_obj->dirty = 1;
390 BUG_ON(from_obj->ring != to->ring);
391 i915_gem_object_unpin(from_obj);
392 }
393
394 ring->last_context_obj = to->obj;
395 to->is_initialized = true;
396
397 return 0;
398}
399
400/**
401 * i915_switch_context() - perform a GPU context switch.
402 * @ring: ring for which we'll execute the context switch
403 * @file_priv: file_priv associated with the context, may be NULL
404 * @id: context id number
405 * @seqno: sequence number by which the new context will be switched to
406 * @flags:
407 *
408 * The context life cycle is simple. The context refcount is incremented and
409 * decremented by 1 and create and destroy. If the context is in use by the GPU,
410 * it will have a refoucnt > 1. This allows us to destroy the context abstract
411 * object while letting the normal object tracking destroy the backing BO.
412 */
413int i915_switch_context(struct intel_ring_buffer *ring,
414 struct drm_file *file,
415 int to_id)
416{
417 struct drm_i915_private *dev_priv = ring->dev->dev_private;
418 struct drm_i915_file_private *file_priv = NULL;
419 struct i915_hw_context *to;
420 struct drm_i915_gem_object *from_obj = ring->last_context_obj;
421 int ret;
422
423 if (dev_priv->hw_contexts_disabled)
424 return 0;
425
426 if (ring != &dev_priv->ring[RCS])
427 return 0;
428
429 if (file)
430 file_priv = file->driver_priv;
431
432 if (to_id == DEFAULT_CONTEXT_ID) {
433 to = ring->default_context;
434 } else {
435 to = i915_gem_context_get(file_priv, to_id);
436 if (to == NULL)
437 return -EINVAL;
438 }
439
440 if (from_obj == to->obj)
441 return 0;
442
443 ret = do_switch(from_obj, to, i915_gem_next_request_seqno(to->ring));
444 if (ret)
445 return ret;
446
447 /* Just to make the code a little cleaner we take the object reference
448 * after the switch was successful. It would be more intuitive to ref
449 * the 'to' object before the switch but we know the refcount must be >0
450 * if context_get() succeeded, and we hold struct mutex. So it's safe to
451 * do this here/now
452 */
453 drm_gem_object_reference(&to->obj->base);
454 if (from_obj != NULL)
455 drm_gem_object_unreference(&from_obj->base);
456 return ret;
457}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index bb19becb1163..b7884b90474a 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -117,6 +117,7 @@ struct intel_ring_buffer {
117 wait_queue_head_t irq_queue; 117 wait_queue_head_t irq_queue;
118 118
119 struct i915_hw_context *default_context; 119 struct i915_hw_context *default_context;
120 struct drm_i915_gem_object *last_context_obj;
120 121
121 void *private; 122 void *private;
122}; 123};