Skip to content

Commit 976b55f

Browse files
committed
drm/i915: Allow a context to define its set of engines
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 <[email protected]> Cc: Tvrtko Ursulin <[email protected]> Reviewed-by: Tvrtko Ursulin <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 7f3f317 commit 976b55f

File tree

6 files changed

+341
-13
lines changed

6 files changed

+341
-13
lines changed

drivers/gpu/drm/i915/i915_gem_context.c

Lines changed: 253 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
#include <drm/i915_drm.h>
9191

9292
#include "gt/intel_lrc_reg.h"
93-
#include "gt/intel_workarounds.h"
9493

9594
#include "i915_drv.h"
9695
#include "i915_globals.h"
@@ -141,15 +140,31 @@ static void lut_close(struct i915_gem_context *ctx)
141140
}
142141

143142
static struct intel_context *
144-
lookup_user_engine(struct i915_gem_context *ctx, u16 class, u16 instance)
143+
lookup_user_engine(struct i915_gem_context *ctx,
144+
unsigned long flags,
145+
const struct i915_engine_class_instance *ci)
146+
#define LOOKUP_USER_INDEX BIT(0)
145147
{
146-
struct intel_engine_cs *engine;
148+
int idx;
147149

148-
engine = intel_engine_lookup_user(ctx->i915, class, instance);
149-
if (!engine)
150+
if (!!(flags & LOOKUP_USER_INDEX) != i915_gem_context_user_engines(ctx))
150151
return ERR_PTR(-EINVAL);
151152

152-
return i915_gem_context_get_engine(ctx, engine->id);
153+
if (!i915_gem_context_user_engines(ctx)) {
154+
struct intel_engine_cs *engine;
155+
156+
engine = intel_engine_lookup_user(ctx->i915,
157+
ci->engine_class,
158+
ci->engine_instance);
159+
if (!engine)
160+
return ERR_PTR(-EINVAL);
161+
162+
idx = engine->id;
163+
} else {
164+
idx = ci->engine_instance;
165+
}
166+
167+
return i915_gem_context_get_engine(ctx, idx);
153168
}
154169

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

275+
static void free_engines_rcu(struct work_struct *wrk)
276+
{
277+
struct i915_gem_engines *e =
278+
container_of(wrk, struct i915_gem_engines, rcu.work);
279+
struct drm_i915_private *i915 = e->i915;
280+
281+
mutex_lock(&i915->drm.struct_mutex);
282+
free_engines(e);
283+
mutex_unlock(&i915->drm.struct_mutex);
284+
}
285+
260286
static struct i915_gem_engines *default_engines(struct i915_gem_context *ctx)
261287
{
262288
struct intel_engine_cs *engine;
@@ -1352,9 +1378,7 @@ static int set_sseu(struct i915_gem_context *ctx,
13521378
if (user_sseu.flags || user_sseu.rsvd)
13531379
return -EINVAL;
13541380

1355-
ce = lookup_user_engine(ctx,
1356-
user_sseu.engine.engine_class,
1357-
user_sseu.engine.engine_instance);
1381+
ce = lookup_user_engine(ctx, 0, &user_sseu.engine);
13581382
if (IS_ERR(ce))
13591383
return PTR_ERR(ce);
13601384

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

1406+
struct set_engines {
1407+
struct i915_gem_context *ctx;
1408+
struct i915_gem_engines *engines;
1409+
};
1410+
1411+
static const i915_user_extension_fn set_engines__extensions[] = {
1412+
};
1413+
1414+
static int
1415+
set_engines(struct i915_gem_context *ctx,
1416+
const struct drm_i915_gem_context_param *args)
1417+
{
1418+
struct i915_context_param_engines __user *user =
1419+
u64_to_user_ptr(args->value);
1420+
struct set_engines set = { .ctx = ctx };
1421+
unsigned int num_engines, n;
1422+
u64 extensions;
1423+
int err;
1424+
1425+
if (!args->size) { /* switch back to legacy user_ring_map */
1426+
if (!i915_gem_context_user_engines(ctx))
1427+
return 0;
1428+
1429+
set.engines = default_engines(ctx);
1430+
if (IS_ERR(set.engines))
1431+
return PTR_ERR(set.engines);
1432+
1433+
goto replace;
1434+
}
1435+
1436+
BUILD_BUG_ON(!IS_ALIGNED(sizeof(*user), sizeof(*user->engines)));
1437+
if (args->size < sizeof(*user) ||
1438+
!IS_ALIGNED(args->size, sizeof(*user->engines))) {
1439+
DRM_DEBUG("Invalid size for engine array: %d\n",
1440+
args->size);
1441+
return -EINVAL;
1442+
}
1443+
1444+
/*
1445+
* Note that I915_EXEC_RING_MASK limits execbuf to only using the
1446+
* first 64 engines defined here.
1447+
*/
1448+
num_engines = (args->size - sizeof(*user)) / sizeof(*user->engines);
1449+
1450+
set.engines = kmalloc(struct_size(set.engines, engines, num_engines),
1451+
GFP_KERNEL);
1452+
if (!set.engines)
1453+
return -ENOMEM;
1454+
1455+
set.engines->i915 = ctx->i915;
1456+
for (n = 0; n < num_engines; n++) {
1457+
struct i915_engine_class_instance ci;
1458+
struct intel_engine_cs *engine;
1459+
1460+
if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) {
1461+
__free_engines(set.engines, n);
1462+
return -EFAULT;
1463+
}
1464+
1465+
if (ci.engine_class == (u16)I915_ENGINE_CLASS_INVALID &&
1466+
ci.engine_instance == (u16)I915_ENGINE_CLASS_INVALID_NONE) {
1467+
set.engines->engines[n] = NULL;
1468+
continue;
1469+
}
1470+
1471+
engine = intel_engine_lookup_user(ctx->i915,
1472+
ci.engine_class,
1473+
ci.engine_instance);
1474+
if (!engine) {
1475+
DRM_DEBUG("Invalid engine[%d]: { class:%d, instance:%d }\n",
1476+
n, ci.engine_class, ci.engine_instance);
1477+
__free_engines(set.engines, n);
1478+
return -ENOENT;
1479+
}
1480+
1481+
set.engines->engines[n] = intel_context_create(ctx, engine);
1482+
if (!set.engines->engines[n]) {
1483+
__free_engines(set.engines, n);
1484+
return -ENOMEM;
1485+
}
1486+
}
1487+
set.engines->num_engines = num_engines;
1488+
1489+
err = -EFAULT;
1490+
if (!get_user(extensions, &user->extensions))
1491+
err = i915_user_extensions(u64_to_user_ptr(extensions),
1492+
set_engines__extensions,
1493+
ARRAY_SIZE(set_engines__extensions),
1494+
&set);
1495+
if (err) {
1496+
free_engines(set.engines);
1497+
return err;
1498+
}
1499+
1500+
replace:
1501+
mutex_lock(&ctx->engines_mutex);
1502+
if (args->size)
1503+
i915_gem_context_set_user_engines(ctx);
1504+
else
1505+
i915_gem_context_clear_user_engines(ctx);
1506+
rcu_swap_protected(ctx->engines, set.engines, 1);
1507+
mutex_unlock(&ctx->engines_mutex);
1508+
1509+
INIT_RCU_WORK(&set.engines->rcu, free_engines_rcu);
1510+
queue_rcu_work(system_wq, &set.engines->rcu);
1511+
1512+
return 0;
1513+
}
1514+
1515+
static struct i915_gem_engines *
1516+
__copy_engines(struct i915_gem_engines *e)
1517+
{
1518+
struct i915_gem_engines *copy;
1519+
unsigned int n;
1520+
1521+
copy = kmalloc(struct_size(e, engines, e->num_engines), GFP_KERNEL);
1522+
if (!copy)
1523+
return ERR_PTR(-ENOMEM);
1524+
1525+
copy->i915 = e->i915;
1526+
for (n = 0; n < e->num_engines; n++) {
1527+
if (e->engines[n])
1528+
copy->engines[n] = intel_context_get(e->engines[n]);
1529+
else
1530+
copy->engines[n] = NULL;
1531+
}
1532+
copy->num_engines = n;
1533+
1534+
return copy;
1535+
}
1536+
1537+
static int
1538+
get_engines(struct i915_gem_context *ctx,
1539+
struct drm_i915_gem_context_param *args)
1540+
{
1541+
struct i915_context_param_engines __user *user;
1542+
struct i915_gem_engines *e;
1543+
size_t n, count, size;
1544+
int err = 0;
1545+
1546+
err = mutex_lock_interruptible(&ctx->engines_mutex);
1547+
if (err)
1548+
return err;
1549+
1550+
e = NULL;
1551+
if (i915_gem_context_user_engines(ctx))
1552+
e = __copy_engines(i915_gem_context_engines(ctx));
1553+
mutex_unlock(&ctx->engines_mutex);
1554+
if (IS_ERR_OR_NULL(e)) {
1555+
args->size = 0;
1556+
return PTR_ERR_OR_ZERO(e);
1557+
}
1558+
1559+
count = e->num_engines;
1560+
1561+
/* Be paranoid in case we have an impedance mismatch */
1562+
if (!check_struct_size(user, engines, count, &size)) {
1563+
err = -EINVAL;
1564+
goto err_free;
1565+
}
1566+
if (overflows_type(size, args->size)) {
1567+
err = -EINVAL;
1568+
goto err_free;
1569+
}
1570+
1571+
if (!args->size) {
1572+
args->size = size;
1573+
goto err_free;
1574+
}
1575+
1576+
if (args->size < size) {
1577+
err = -EINVAL;
1578+
goto err_free;
1579+
}
1580+
1581+
user = u64_to_user_ptr(args->value);
1582+
if (!access_ok(user, size)) {
1583+
err = -EFAULT;
1584+
goto err_free;
1585+
}
1586+
1587+
if (put_user(0, &user->extensions)) {
1588+
err = -EFAULT;
1589+
goto err_free;
1590+
}
1591+
1592+
for (n = 0; n < count; n++) {
1593+
struct i915_engine_class_instance ci = {
1594+
.engine_class = I915_ENGINE_CLASS_INVALID,
1595+
.engine_instance = I915_ENGINE_CLASS_INVALID_NONE,
1596+
};
1597+
1598+
if (e->engines[n]) {
1599+
ci.engine_class = e->engines[n]->engine->uabi_class;
1600+
ci.engine_instance = e->engines[n]->engine->instance;
1601+
}
1602+
1603+
if (copy_to_user(&user->engines[n], &ci, sizeof(ci))) {
1604+
err = -EFAULT;
1605+
goto err_free;
1606+
}
1607+
}
1608+
1609+
args->size = size;
1610+
1611+
err_free:
1612+
INIT_RCU_WORK(&e->rcu, free_engines_rcu);
1613+
queue_rcu_work(system_wq, &e->rcu);
1614+
return err;
1615+
}
1616+
13821617
static int ctx_setparam(struct drm_i915_file_private *fpriv,
13831618
struct i915_gem_context *ctx,
13841619
struct drm_i915_gem_context_param *args)
@@ -1452,6 +1687,10 @@ static int ctx_setparam(struct drm_i915_file_private *fpriv,
14521687
ret = set_ppgtt(fpriv, ctx, args);
14531688
break;
14541689

1690+
case I915_CONTEXT_PARAM_ENGINES:
1691+
ret = set_engines(ctx, args);
1692+
break;
1693+
14551694
case I915_CONTEXT_PARAM_BAN_PERIOD:
14561695
default:
14571696
ret = -EINVAL;
@@ -1596,9 +1835,7 @@ static int get_sseu(struct i915_gem_context *ctx,
15961835
if (user_sseu.flags || user_sseu.rsvd)
15971836
return -EINVAL;
15981837

1599-
ce = lookup_user_engine(ctx,
1600-
user_sseu.engine.engine_class,
1601-
user_sseu.engine.engine_instance);
1838+
ce = lookup_user_engine(ctx, 0, &user_sseu.engine);
16021839
if (IS_ERR(ce))
16031840
return PTR_ERR(ce);
16041841

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

1922+
case I915_CONTEXT_PARAM_ENGINES:
1923+
ret = get_engines(ctx, args);
1924+
break;
1925+
16851926
case I915_CONTEXT_PARAM_BAN_PERIOD:
16861927
default:
16871928
ret = -EINVAL;

drivers/gpu/drm/i915/i915_gem_context.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ static inline void i915_gem_context_set_force_single_submission(struct i915_gem_
112112
__set_bit(CONTEXT_FORCE_SINGLE_SUBMISSION, &ctx->flags);
113113
}
114114

115+
static inline bool
116+
i915_gem_context_user_engines(const struct i915_gem_context *ctx)
117+
{
118+
return test_bit(CONTEXT_USER_ENGINES, &ctx->flags);
119+
}
120+
121+
static inline void
122+
i915_gem_context_set_user_engines(struct i915_gem_context *ctx)
123+
{
124+
set_bit(CONTEXT_USER_ENGINES, &ctx->flags);
125+
}
126+
127+
static inline void
128+
i915_gem_context_clear_user_engines(struct i915_gem_context *ctx)
129+
{
130+
clear_bit(CONTEXT_USER_ENGINES, &ctx->flags);
131+
}
132+
115133
int __i915_gem_context_pin_hw_id(struct i915_gem_context *ctx);
116134
static inline int i915_gem_context_pin_hw_id(struct i915_gem_context *ctx)
117135
{

drivers/gpu/drm/i915/i915_gem_context_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ struct i915_gem_context {
146146
#define CONTEXT_BANNED 0
147147
#define CONTEXT_CLOSED 1
148148
#define CONTEXT_FORCE_SINGLE_SUBMISSION 2
149+
#define CONTEXT_USER_ENGINES 3
149150

150151
/**
151152
* @hw_id: - unique identifier for the context

drivers/gpu/drm/i915/i915_gem_execbuffer.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2165,7 +2165,10 @@ eb_select_engine(struct i915_execbuffer *eb,
21652165
unsigned int idx;
21662166
int err;
21672167

2168-
idx = eb_select_legacy_ring(eb, file, args);
2168+
if (i915_gem_context_user_engines(eb->gem_context))
2169+
idx = args->flags & I915_EXEC_RING_MASK;
2170+
else
2171+
idx = eb_select_legacy_ring(eb, file, args);
21692172

21702173
ce = i915_gem_context_get_engine(eb->gem_context, idx);
21712174
if (IS_ERR(ce))

0 commit comments

Comments
 (0)