Skip to content

Commit e7af311

Browse files
committed
drm/i915: Introduce a preempt context
Add another perma-pinned context for using for preemption at any time. We cannot just reuse the existing kernel context, as first and foremost we need to ensure that we can preempt the kernel context itself, so require a distinct context id. Similar to the kernel context, we may want to interrupt execution and switch to the preempt context at any time, and so it needs to be permanently pinned and available. To compensate for yet another permanent allocation, we shrink the existing context and the new context by reducing their ringbuffer to the minimum. v2: Assert that we never allocate a request from the preemption context. v3: Limit perma-pin to engines that may preempt. v4: Onion cleanup for early driver death v5: Onion ordering in main driver cleanup as well. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Michał Winiarski <michal.winiarski@intel.com> Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20171003203453.15692-4-chris@chris-wilson.co.uk
1 parent d6c0511 commit e7af311

File tree

4 files changed

+87
-24
lines changed

4 files changed

+87
-24
lines changed

drivers/gpu/drm/i915/i915_drv.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ struct intel_csr {
783783
func(has_l3_dpf); \
784784
func(has_llc); \
785785
func(has_logical_ring_contexts); \
786+
func(has_logical_ring_preemption); \
786787
func(has_overlay); \
787788
func(has_pipe_cxsr); \
788789
func(has_pooled_eu); \
@@ -2251,8 +2252,11 @@ struct drm_i915_private {
22512252
wait_queue_head_t gmbus_wait_queue;
22522253

22532254
struct pci_dev *bridge_dev;
2254-
struct i915_gem_context *kernel_context;
22552255
struct intel_engine_cs *engine[I915_NUM_ENGINES];
2256+
/* Context used internally to idle the GPU and setup initial state */
2257+
struct i915_gem_context *kernel_context;
2258+
/* Context only to be used for injecting preemption commands */
2259+
struct i915_gem_context *preempt_context;
22562260
struct i915_vma *semaphore;
22572261

22582262
struct drm_dma_handle *status_page_dmah;

drivers/gpu/drm/i915/i915_gem_context.c

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,43 @@ i915_gem_context_create_gvt(struct drm_device *dev)
416416
return ctx;
417417
}
418418

419+
static struct i915_gem_context *
420+
create_kernel_context(struct drm_i915_private *i915, int prio)
421+
{
422+
struct i915_gem_context *ctx;
423+
424+
ctx = i915_gem_create_context(i915, NULL);
425+
if (IS_ERR(ctx))
426+
return ctx;
427+
428+
i915_gem_context_clear_bannable(ctx);
429+
ctx->priority = prio;
430+
ctx->ring_size = PAGE_SIZE;
431+
432+
GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
433+
434+
return ctx;
435+
}
436+
437+
static void
438+
destroy_kernel_context(struct i915_gem_context **ctxp)
439+
{
440+
struct i915_gem_context *ctx;
441+
442+
/* Keep the context ref so that we can free it immediately ourselves */
443+
ctx = i915_gem_context_get(fetch_and_zero(ctxp));
444+
GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
445+
446+
context_close(ctx);
447+
i915_gem_context_free(ctx);
448+
}
449+
419450
int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
420451
{
421452
struct i915_gem_context *ctx;
453+
int err;
422454

423-
/* Init should only be called once per module load. Eventually the
424-
* restriction on the context_disabled check can be loosened. */
425-
if (WARN_ON(dev_priv->kernel_context))
426-
return 0;
455+
GEM_BUG_ON(dev_priv->kernel_context);
427456

428457
INIT_LIST_HEAD(&dev_priv->contexts.list);
429458
INIT_WORK(&dev_priv->contexts.free_work, contexts_free_worker);
@@ -441,28 +470,38 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
441470
BUILD_BUG_ON(MAX_CONTEXT_HW_ID > INT_MAX);
442471
ida_init(&dev_priv->contexts.hw_ida);
443472

444-
ctx = i915_gem_create_context(dev_priv, NULL);
473+
/* lowest priority; idle task */
474+
ctx = create_kernel_context(dev_priv, I915_PRIORITY_MIN);
445475
if (IS_ERR(ctx)) {
446-
DRM_ERROR("Failed to create default global context (error %ld)\n",
447-
PTR_ERR(ctx));
448-
return PTR_ERR(ctx);
476+
DRM_ERROR("Failed to create default global context\n");
477+
err = PTR_ERR(ctx);
478+
goto err;
449479
}
450-
451-
/* For easy recognisablity, we want the kernel context to be 0 and then
480+
/*
481+
* For easy recognisablity, we want the kernel context to be 0 and then
452482
* all user contexts will have non-zero hw_id.
453483
*/
454484
GEM_BUG_ON(ctx->hw_id);
455-
456-
i915_gem_context_clear_bannable(ctx);
457-
ctx->priority = I915_PRIORITY_MIN; /* lowest priority; idle task */
458485
dev_priv->kernel_context = ctx;
459486

460-
GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
487+
/* highest priority; preempting task */
488+
ctx = create_kernel_context(dev_priv, INT_MAX);
489+
if (IS_ERR(ctx)) {
490+
DRM_ERROR("Failed to create default preempt context\n");
491+
err = PTR_ERR(ctx);
492+
goto err_kernel_context;
493+
}
494+
dev_priv->preempt_context = ctx;
461495

462496
DRM_DEBUG_DRIVER("%s context support initialized\n",
463497
dev_priv->engine[RCS]->context_size ? "logical" :
464498
"fake");
465499
return 0;
500+
501+
err_kernel_context:
502+
destroy_kernel_context(&dev_priv->kernel_context);
503+
err:
504+
return err;
466505
}
467506

468507
void i915_gem_contexts_lost(struct drm_i915_private *dev_priv)
@@ -507,15 +546,10 @@ void i915_gem_contexts_lost(struct drm_i915_private *dev_priv)
507546

508547
void i915_gem_contexts_fini(struct drm_i915_private *i915)
509548
{
510-
struct i915_gem_context *ctx;
511-
512549
lockdep_assert_held(&i915->drm.struct_mutex);
513550

514-
/* Keep the context so that we can free it immediately ourselves */
515-
ctx = i915_gem_context_get(fetch_and_zero(&i915->kernel_context));
516-
GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
517-
context_close(ctx);
518-
i915_gem_context_free(ctx);
551+
destroy_kernel_context(&i915->preempt_context);
552+
destroy_kernel_context(&i915->kernel_context);
519553

520554
/* Must free all deferred contexts (via flush_workqueue) first */
521555
ida_destroy(&i915->contexts.hw_ida);

drivers/gpu/drm/i915/i915_gem_request.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,13 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
587587

588588
lockdep_assert_held(&dev_priv->drm.struct_mutex);
589589

590+
/*
591+
* Preempt contexts are reserved for exclusive use to inject a
592+
* preemption context switch. They are never to be used for any trivial
593+
* request!
594+
*/
595+
GEM_BUG_ON(ctx == dev_priv->preempt_context);
596+
590597
/* ABI: Before userspace accesses the GPU (e.g. execbuffer), report
591598
* EIO if the GPU is already wedged.
592599
*/

drivers/gpu/drm/i915/intel_engine_cs.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,22 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
613613
if (IS_ERR(ring))
614614
return PTR_ERR(ring);
615615

616+
/*
617+
* Similarly the preempt context must always be available so that
618+
* we can interrupt the engine at any time.
619+
*/
620+
if (INTEL_INFO(engine->i915)->has_logical_ring_preemption) {
621+
ring = engine->context_pin(engine,
622+
engine->i915->preempt_context);
623+
if (IS_ERR(ring)) {
624+
ret = PTR_ERR(ring);
625+
goto err_unpin_kernel;
626+
}
627+
}
628+
616629
ret = intel_engine_init_breadcrumbs(engine);
617630
if (ret)
618-
goto err_unpin;
631+
goto err_unpin_preempt;
619632

620633
ret = i915_gem_render_state_init(engine);
621634
if (ret)
@@ -634,7 +647,10 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
634647
i915_gem_render_state_fini(engine);
635648
err_breadcrumbs:
636649
intel_engine_fini_breadcrumbs(engine);
637-
err_unpin:
650+
err_unpin_preempt:
651+
if (INTEL_INFO(engine->i915)->has_logical_ring_preemption)
652+
engine->context_unpin(engine, engine->i915->preempt_context);
653+
err_unpin_kernel:
638654
engine->context_unpin(engine, engine->i915->kernel_context);
639655
return ret;
640656
}
@@ -660,6 +676,8 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
660676
intel_engine_cleanup_cmd_parser(engine);
661677
i915_gem_batch_pool_fini(&engine->batch_pool);
662678

679+
if (INTEL_INFO(engine->i915)->has_logical_ring_preemption)
680+
engine->context_unpin(engine, engine->i915->preempt_context);
663681
engine->context_unpin(engine, engine->i915->kernel_context);
664682
}
665683

0 commit comments

Comments
 (0)