Skip to content

Commit

Permalink
drm/i915: Allow a context to define its set of engines
Browse files Browse the repository at this point in the history
Over the last few years, we have debated how to extend the user API to
support an increase in the number of engines, that may be sparse and
even be heterogeneous within a class (not all video decoders created
equal). We settled on using (class, instance) tuples to identify a
specific engine, with an API for the user to construct a map of engines
to capabilities. Into this picture, we then add a challenge of virtual
engines; one user engine that maps behind the scenes to any number of
physical engines. To keep it general, we want the user to have full
control over that mapping. To that end, we allow the user to constrain a
context to define the set of engines that it can access, order fully
controlled by the user via (class, instance). With such precise control
in context setup, we can continue to use the existing execbuf uABI of
specifying a single index; only now it doesn't automagically map onto
the engines, it uses the user defined engine map from the context.

v2: Fixup freeing of local on success of get_engines()
v3: Allow empty engines[]
v4: s/nengine/num_engines/
v5: Replace 64 limit on num_engines with a note that execbuf is
currently limited to only using the first 64 engines.
v6: Actually use the engines_mutex to guard the ctx->engines.

Testcase: igt/gem_ctx_engines
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190521211134.16117-2-chris@chris-wilson.co.uk
  • Loading branch information
Chris Wilson committed May 22, 2019
1 parent 7f3f317 commit 976b55f
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 13 deletions.
265 changes: 253 additions & 12 deletions drivers/gpu/drm/i915/i915_gem_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
#include <drm/i915_drm.h>

#include "gt/intel_lrc_reg.h"
#include "gt/intel_workarounds.h"

#include "i915_drv.h"
#include "i915_globals.h"
Expand Down Expand Up @@ -141,15 +140,31 @@ static void lut_close(struct i915_gem_context *ctx)
}

static struct intel_context *
lookup_user_engine(struct i915_gem_context *ctx, u16 class, u16 instance)
lookup_user_engine(struct i915_gem_context *ctx,
unsigned long flags,
const struct i915_engine_class_instance *ci)
#define LOOKUP_USER_INDEX BIT(0)
{
struct intel_engine_cs *engine;
int idx;

engine = intel_engine_lookup_user(ctx->i915, class, instance);
if (!engine)
if (!!(flags & LOOKUP_USER_INDEX) != i915_gem_context_user_engines(ctx))
return ERR_PTR(-EINVAL);

return i915_gem_context_get_engine(ctx, engine->id);
if (!i915_gem_context_user_engines(ctx)) {
struct intel_engine_cs *engine;

engine = intel_engine_lookup_user(ctx->i915,
ci->engine_class,
ci->engine_instance);
if (!engine)
return ERR_PTR(-EINVAL);

idx = engine->id;
} else {
idx = ci->engine_instance;
}

return i915_gem_context_get_engine(ctx, idx);
}

static inline int new_hw_id(struct drm_i915_private *i915, gfp_t gfp)
Expand Down Expand Up @@ -257,6 +272,17 @@ static void free_engines(struct i915_gem_engines *e)
__free_engines(e, e->num_engines);
}

static void free_engines_rcu(struct work_struct *wrk)
{
struct i915_gem_engines *e =
container_of(wrk, struct i915_gem_engines, rcu.work);
struct drm_i915_private *i915 = e->i915;

mutex_lock(&i915->drm.struct_mutex);
free_engines(e);
mutex_unlock(&i915->drm.struct_mutex);
}

static struct i915_gem_engines *default_engines(struct i915_gem_context *ctx)
{
struct intel_engine_cs *engine;
Expand Down Expand Up @@ -1352,9 +1378,7 @@ static int set_sseu(struct i915_gem_context *ctx,
if (user_sseu.flags || user_sseu.rsvd)
return -EINVAL;

ce = lookup_user_engine(ctx,
user_sseu.engine.engine_class,
user_sseu.engine.engine_instance);
ce = lookup_user_engine(ctx, 0, &user_sseu.engine);
if (IS_ERR(ce))
return PTR_ERR(ce);

Expand All @@ -1379,6 +1403,217 @@ static int set_sseu(struct i915_gem_context *ctx,
return ret;
}

struct set_engines {
struct i915_gem_context *ctx;
struct i915_gem_engines *engines;
};

static const i915_user_extension_fn set_engines__extensions[] = {
};

static int
set_engines(struct i915_gem_context *ctx,
const struct drm_i915_gem_context_param *args)
{
struct i915_context_param_engines __user *user =
u64_to_user_ptr(args->value);
struct set_engines set = { .ctx = ctx };
unsigned int num_engines, n;
u64 extensions;
int err;

if (!args->size) { /* switch back to legacy user_ring_map */
if (!i915_gem_context_user_engines(ctx))
return 0;

set.engines = default_engines(ctx);
if (IS_ERR(set.engines))
return PTR_ERR(set.engines);

goto replace;
}

BUILD_BUG_ON(!IS_ALIGNED(sizeof(*user), sizeof(*user->engines)));
if (args->size < sizeof(*user) ||
!IS_ALIGNED(args->size, sizeof(*user->engines))) {
DRM_DEBUG("Invalid size for engine array: %d\n",
args->size);
return -EINVAL;
}

/*
* Note that I915_EXEC_RING_MASK limits execbuf to only using the
* first 64 engines defined here.
*/
num_engines = (args->size - sizeof(*user)) / sizeof(*user->engines);

set.engines = kmalloc(struct_size(set.engines, engines, num_engines),
GFP_KERNEL);
if (!set.engines)
return -ENOMEM;

set.engines->i915 = ctx->i915;
for (n = 0; n < num_engines; n++) {
struct i915_engine_class_instance ci;
struct intel_engine_cs *engine;

if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) {
__free_engines(set.engines, n);
return -EFAULT;
}

if (ci.engine_class == (u16)I915_ENGINE_CLASS_INVALID &&
ci.engine_instance == (u16)I915_ENGINE_CLASS_INVALID_NONE) {
set.engines->engines[n] = NULL;
continue;
}

engine = intel_engine_lookup_user(ctx->i915,
ci.engine_class,
ci.engine_instance);
if (!engine) {
DRM_DEBUG("Invalid engine[%d]: { class:%d, instance:%d }\n",
n, ci.engine_class, ci.engine_instance);
__free_engines(set.engines, n);
return -ENOENT;
}

set.engines->engines[n] = intel_context_create(ctx, engine);
if (!set.engines->engines[n]) {
__free_engines(set.engines, n);
return -ENOMEM;
}
}
set.engines->num_engines = num_engines;

err = -EFAULT;
if (!get_user(extensions, &user->extensions))
err = i915_user_extensions(u64_to_user_ptr(extensions),
set_engines__extensions,
ARRAY_SIZE(set_engines__extensions),
&set);
if (err) {
free_engines(set.engines);
return err;
}

replace:
mutex_lock(&ctx->engines_mutex);
if (args->size)
i915_gem_context_set_user_engines(ctx);
else
i915_gem_context_clear_user_engines(ctx);
rcu_swap_protected(ctx->engines, set.engines, 1);
mutex_unlock(&ctx->engines_mutex);

INIT_RCU_WORK(&set.engines->rcu, free_engines_rcu);
queue_rcu_work(system_wq, &set.engines->rcu);

return 0;
}

static struct i915_gem_engines *
__copy_engines(struct i915_gem_engines *e)
{
struct i915_gem_engines *copy;
unsigned int n;

copy = kmalloc(struct_size(e, engines, e->num_engines), GFP_KERNEL);
if (!copy)
return ERR_PTR(-ENOMEM);

copy->i915 = e->i915;
for (n = 0; n < e->num_engines; n++) {
if (e->engines[n])
copy->engines[n] = intel_context_get(e->engines[n]);
else
copy->engines[n] = NULL;
}
copy->num_engines = n;

return copy;
}

static int
get_engines(struct i915_gem_context *ctx,
struct drm_i915_gem_context_param *args)
{
struct i915_context_param_engines __user *user;
struct i915_gem_engines *e;
size_t n, count, size;
int err = 0;

err = mutex_lock_interruptible(&ctx->engines_mutex);
if (err)
return err;

e = NULL;
if (i915_gem_context_user_engines(ctx))
e = __copy_engines(i915_gem_context_engines(ctx));
mutex_unlock(&ctx->engines_mutex);
if (IS_ERR_OR_NULL(e)) {
args->size = 0;
return PTR_ERR_OR_ZERO(e);
}

count = e->num_engines;

/* Be paranoid in case we have an impedance mismatch */
if (!check_struct_size(user, engines, count, &size)) {
err = -EINVAL;
goto err_free;
}
if (overflows_type(size, args->size)) {
err = -EINVAL;
goto err_free;
}

if (!args->size) {
args->size = size;
goto err_free;
}

if (args->size < size) {
err = -EINVAL;
goto err_free;
}

user = u64_to_user_ptr(args->value);
if (!access_ok(user, size)) {
err = -EFAULT;
goto err_free;
}

if (put_user(0, &user->extensions)) {
err = -EFAULT;
goto err_free;
}

for (n = 0; n < count; n++) {
struct i915_engine_class_instance ci = {
.engine_class = I915_ENGINE_CLASS_INVALID,
.engine_instance = I915_ENGINE_CLASS_INVALID_NONE,
};

if (e->engines[n]) {
ci.engine_class = e->engines[n]->engine->uabi_class;
ci.engine_instance = e->engines[n]->engine->instance;
}

if (copy_to_user(&user->engines[n], &ci, sizeof(ci))) {
err = -EFAULT;
goto err_free;
}
}

args->size = size;

err_free:
INIT_RCU_WORK(&e->rcu, free_engines_rcu);
queue_rcu_work(system_wq, &e->rcu);
return err;
}

static int ctx_setparam(struct drm_i915_file_private *fpriv,
struct i915_gem_context *ctx,
struct drm_i915_gem_context_param *args)
Expand Down Expand Up @@ -1452,6 +1687,10 @@ static int ctx_setparam(struct drm_i915_file_private *fpriv,
ret = set_ppgtt(fpriv, ctx, args);
break;

case I915_CONTEXT_PARAM_ENGINES:
ret = set_engines(ctx, args);
break;

case I915_CONTEXT_PARAM_BAN_PERIOD:
default:
ret = -EINVAL;
Expand Down Expand Up @@ -1596,9 +1835,7 @@ static int get_sseu(struct i915_gem_context *ctx,
if (user_sseu.flags || user_sseu.rsvd)
return -EINVAL;

ce = lookup_user_engine(ctx,
user_sseu.engine.engine_class,
user_sseu.engine.engine_instance);
ce = lookup_user_engine(ctx, 0, &user_sseu.engine);
if (IS_ERR(ce))
return PTR_ERR(ce);

Expand Down Expand Up @@ -1682,6 +1919,10 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
ret = get_ppgtt(file_priv, ctx, args);
break;

case I915_CONTEXT_PARAM_ENGINES:
ret = get_engines(ctx, args);
break;

case I915_CONTEXT_PARAM_BAN_PERIOD:
default:
ret = -EINVAL;
Expand Down
18 changes: 18 additions & 0 deletions drivers/gpu/drm/i915/i915_gem_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ static inline void i915_gem_context_set_force_single_submission(struct i915_gem_
__set_bit(CONTEXT_FORCE_SINGLE_SUBMISSION, &ctx->flags);
}

static inline bool
i915_gem_context_user_engines(const struct i915_gem_context *ctx)
{
return test_bit(CONTEXT_USER_ENGINES, &ctx->flags);
}

static inline void
i915_gem_context_set_user_engines(struct i915_gem_context *ctx)
{
set_bit(CONTEXT_USER_ENGINES, &ctx->flags);
}

static inline void
i915_gem_context_clear_user_engines(struct i915_gem_context *ctx)
{
clear_bit(CONTEXT_USER_ENGINES, &ctx->flags);
}

int __i915_gem_context_pin_hw_id(struct i915_gem_context *ctx);
static inline int i915_gem_context_pin_hw_id(struct i915_gem_context *ctx)
{
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_gem_context_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ struct i915_gem_context {
#define CONTEXT_BANNED 0
#define CONTEXT_CLOSED 1
#define CONTEXT_FORCE_SINGLE_SUBMISSION 2
#define CONTEXT_USER_ENGINES 3

/**
* @hw_id: - unique identifier for the context
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/i915/i915_gem_execbuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -2165,7 +2165,10 @@ eb_select_engine(struct i915_execbuffer *eb,
unsigned int idx;
int err;

idx = eb_select_legacy_ring(eb, file, args);
if (i915_gem_context_user_engines(eb->gem_context))
idx = args->flags & I915_EXEC_RING_MASK;
else
idx = eb_select_legacy_ring(eb, file, args);

ce = i915_gem_context_get_engine(eb->gem_context, idx);
if (IS_ERR(ce))
Expand Down
Loading

0 comments on commit 976b55f

Please sign in to comment.