[PATCH 31/49] drm/i915/bdw: Introduce dependent contexts

oscar.mateo
2014-03-27T17:08:24Z

From: Oscar Mateo <oscar.mateo@intel.com>

>From here on, we define a stand-alone context as the first context with
a given ID to be created for a new fd or a new context create ioctl. This
is the one we can easily find using integer ID management. On the other
hand, dependent contexts are subsequently created with the same ID and
simply hang from the stand-alone one.

This patch, together with the two previous and the next, are meant to
solve a big problem we have: with execlists, we need contexts to work with
all engines, and we cannot reuse one context for more than one engine.

Because, on a new fd or a context create ioctl, we really don't know which
engine is going to be used later on, we are going to create at that point
a "blank" context and assign it to an engine on a deferred way (during the
execbuffer, to be precise). If later on, we execbuffer on a different engine,
we create a new dependent context on the previous.

Note: I have tried to colour this patch in a different way, using a different
struct (a "context group") to hold the context ID from where the per-engine
contexts hang, but it makes legacy contexts unnecessary complex.

Signed-off-by: Oscar Mateo <oscar.mateo@intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 6 +++++-
drivers/gpu/drm/i915/i915_gem_context.c | 17 +++++++++++++--
drivers/gpu/drm/i915/i915_lrc.c | 37 ++++++++++++++++++++++++++++++---
3 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 91b0886..d9470a4 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -602,6 +602,9 @@ struct i915_hw_context {
struct i915_address_space *vm;

struct list_head link;
+
+ /* Advanced contexts only */
+ struct list_head dependent_contexts;
};

struct i915_fbc {
@@ -2321,7 +2324,8 @@ int gen8_gem_context_init(struct drm_device *dev);
void gen8_gem_context_fini(struct drm_device *dev);
struct i915_hw_context *gen8_gem_create_context(struct drm_device *dev,
struct intel_engine *ring,
- struct drm_i915_file_private *file_priv, bool create_vm);
+ 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);

/* i915_gem_evict.c */
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 6baa5ab..17015b2 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -271,6 +271,8 @@ __create_hw_context(struct drm_device *dev,
* is no remap info, it will be a NOP. */
ctx->remap_slice = (1 << NUM_L3_SLICES(dev)) - 1;

+ INIT_LIST_HEAD(&ctx->dependent_contexts);
+
return ctx;

err_out:
@@ -511,6 +513,12 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
static int context_idr_cleanup(int id, void *p, void *data)
{
struct i915_hw_context *ctx = p;
+ struct i915_hw_context *cursor, *tmp;
+
+ list_for_each_entry_safe(cursor, tmp, &ctx->dependent_contexts, dependent_contexts) {
+ list_del(&cursor->dependent_contexts);
+ i915_gem_context_unreference(cursor);
+ }

/* Ignore the default context because close will handle it */
if (i915_gem_context_is_default(ctx))
@@ -543,7 +551,7 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
if (dev_priv->lrc_enabled)
file_priv->private_default_ctx = gen8_gem_create_context(dev,
&dev_priv->ring[RCS], file_priv,
- USES_FULL_PPGTT(dev));
+ NULL, USES_FULL_PPGTT(dev));
else
file_priv->private_default_ctx = i915_gem_create_context(dev,
file_priv, USES_FULL_PPGTT(dev));
@@ -805,7 +813,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,

if (dev_priv->lrc_enabled)
ctx = gen8_gem_create_context(dev, &dev_priv->ring[RCS],
- file_priv, USES_FULL_PPGTT(dev));
+ file_priv, NULL, USES_FULL_PPGTT(dev));
else
ctx = i915_gem_create_context(dev, file_priv,
USES_FULL_PPGTT(dev));
@@ -825,6 +833,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_i915_gem_context_destroy *args = data;
struct drm_i915_file_private *file_priv = file->driver_priv;
struct i915_hw_context *ctx;
+ struct i915_hw_context *cursor, *tmp;
int ret;

if (args->ctx_id == DEFAULT_CONTEXT_ID)
@@ -841,6 +850,10 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
}

idr_remove(&ctx->file_priv->context_idr, ctx->id);
+ list_for_each_entry_safe(cursor, tmp, &ctx->dependent_contexts, dependent_contexts) {
+ list_del(&cursor->dependent_contexts);
+ i915_gem_context_unreference(cursor);
+ }
i915_gem_context_unreference(ctx);
mutex_unlock(&dev->struct_mutex);

diff --git a/drivers/gpu/drm/i915/i915_lrc.c b/drivers/gpu/drm/i915/i915_lrc.c
index 124e5f2..99011cc 100644
--- a/drivers/gpu/drm/i915/i915_lrc.c
+++ b/drivers/gpu/drm/i915/i915_lrc.c
@@ -195,23 +195,54 @@ intel_populate_lrc(struct i915_hw_context *ctx,
return 0;
}

+static void assert_on_ppgtt_release(struct kref *kref)
+{
+ WARN(1, "Are we trying to free the aliasing PPGTT?\n");
+}
+
struct i915_hw_context *
gen8_gem_create_context(struct drm_device *dev,
struct intel_engine *ring,
struct drm_i915_file_private *file_priv,
+ struct i915_hw_context *standalone_ctx,
bool create_vm)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_hw_context *ctx = NULL;
struct drm_i915_gem_object *ring_obj = NULL;
struct intel_ringbuffer *ringbuf = NULL;
+ bool is_dependent;
int ret;

- ctx = i915_gem_create_context(dev, file_priv, create_vm);
+ /* NB: a standalone context is the first context with a given id to be
+ * created for a new fd. Dependent contexts simply hang from the stand-alone,
+ * sharing their ID and their PPGTT */
+ is_dependent = (file_priv != NULL) && (standalone_ctx != NULL);
+
+ ctx = i915_gem_create_context(dev, is_dependent? NULL : file_priv,
+ is_dependent? false : create_vm);
if (IS_ERR_OR_NULL(ctx))
return ctx;

- if (file_priv) {
+ if (is_dependent) {
+ struct i915_hw_ppgtt *ppgtt;
+
+ /* We take the same PPGTT as the standalone */
+ ppgtt = ctx_to_ppgtt(ctx);
+ kref_put(&ppgtt->ref, assert_on_ppgtt_release);
+ ppgtt = ctx_to_ppgtt(standalone_ctx);
+ ctx->vm = &ppgtt->base;
+ kref_get(&ppgtt->ref);
+
+ ctx->file_priv = file_priv;
+ ctx->id = standalone_ctx->id;
+ ctx->remap_slice = standalone_ctx->remap_slice;
+
+ list_add_tail(&ctx->dependent_contexts,
+ &standalone_ctx->dependent_contexts);
+ }
+
+ if (file_priv && !is_dependent) {
ret = i915_gem_obj_ggtt_pin(ctx->obj, GEN8_CONTEXT_ALIGN, 0);
if (ret) {
DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
@@ -337,7 +368,7 @@ int gen8_gem_context_init(struct drm_device *dev)

for_each_ring(ring, dev_priv, ring_id) {
ring->default_context = gen8_gem_create_context(dev, ring,
- NULL, (ring_id == RCS));
+ NULL, NULL, (ring_id == RCS));
if (IS_ERR_OR_NULL(ring->default_context)) {
ret = PTR_ERR(ring->default_context);
DRM_DEBUG_DRIVER("Create ctx failed: %d\n", ret);
--
1.9.0