[PATCH 52/53] virtual-bond
Chris Wilson
chris at chris-wilson.co.uk
Tue Sep 18 10:51:04 UTC 2018
Don't mention preemption and unsubmit.
---
drivers/gpu/drm/i915/i915_gem_context.c | 45 ++++++
drivers/gpu/drm/i915/i915_request.c | 1 +
drivers/gpu/drm/i915/i915_request.h | 1 +
drivers/gpu/drm/i915/intel_lrc.c | 81 +++++++++-
drivers/gpu/drm/i915/intel_lrc.h | 3 +
drivers/gpu/drm/i915/intel_ringbuffer.h | 7 +
drivers/gpu/drm/i915/selftests/intel_lrc.c | 167 +++++++++++++++++++++
include/uapi/drm/i915_drm.h | 16 ++
8 files changed, 320 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 6d544171b57c..9f4b68a7accd 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -957,6 +957,16 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
return ret;
}
+static int check_user_mbz32(u32 __user *user)
+{
+ u32 mbz;
+
+ if (get_user(mbz, user))
+ return -EFAULT;
+
+ return mbz ? -EINVAL : 0;
+}
+
static int check_user_mbz64(u64 __user *user)
{
u64 mbz;
@@ -1031,8 +1041,43 @@ static int set_engines__load_balance(struct i915_user_extension __user *base,
return 0;
}
+static int set_engines__bond(struct i915_user_extension __user *base,
+ void *data)
+{
+ struct i915_context_engines_bond __user *ext =
+ container_of(base, typeof(*ext) __user, base);
+ const struct set_engines *set = data;
+ struct intel_engine_cs *master;
+ u32 class, instance, siblings;
+ int err;
+
+ if (!set->engines[0])
+ return -EINVAL;
+
+ err = check_user_mbz32(&ext->flags);
+ if (err)
+ return err;
+
+ if (get_user(class, &ext->master_class))
+ return -EFAULT;
+
+ if (get_user(instance, &ext->master_instance))
+ return -EFAULT;
+
+ master = intel_engine_lookup_user(set->ctx->i915, class, instance);
+ if (!master)
+ return -EINVAL;
+
+ if (get_user(siblings, &ext->sibling_mask))
+ return -EFAULT;
+
+ return intel_virtual_engine_attach_bond(set->engines[0],
+ master, siblings);
+}
+
static const i915_user_extension_fn set_engines__extensions[] = {
[I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE] = set_engines__load_balance,
+ [I915_CONTEXT_ENGINES_EXT_BOND] = set_engines__bond,
};
static int set_engines(struct i915_gem_context *ctx,
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 5213e3c700fe..1da24f9e340a 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -650,6 +650,7 @@ i915_request_alloc(struct intel_engine_cs *engine, struct i915_gem_context *ctx)
/* No zalloc, must clear what we need by hand */
rq->global_seqno = 0;
rq->signaling.wait.seqno = 0;
+ rq->execution_mask = ~0u;
rq->file_priv = NULL;
rq->batch = NULL;
rq->capture_list = NULL;
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index 64864673349c..b9e6b3a5d55c 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -133,6 +133,7 @@ struct i915_request {
*/
struct i915_sched_node sched;
struct i915_dependency dep;
+ unsigned int execution_mask;
/**
* GEM sequence number associated with this request on the
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 2117e98e2644..7773edd293a8 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -178,6 +178,12 @@ struct virtual_engine {
int prio;
} nodes[I915_NUM_ENGINES];
+ struct ve_bond {
+ struct intel_engine_cs *master;
+ unsigned int sibling_mask;
+ } *bonds;
+ unsigned int nbond;
+
unsigned int count;
struct intel_engine_cs *siblings[0];
};
@@ -2955,6 +2961,7 @@ virtual_context_pin(struct intel_engine_cs *engine,
static void virtual_submission_tasklet(unsigned long data)
{
struct virtual_engine * const ve = (struct virtual_engine *)data;
+ unsigned int mask;
unsigned int n;
int prio;
@@ -2962,18 +2969,33 @@ static void virtual_submission_tasklet(unsigned long data)
prio = I915_PRIORITY_INVALID;
spin_lock(&ve->base.timeline.lock);
- if (ve->request)
+ if (ve->request) {
prio = rq_prio(ve->request);
+ mask = ve->request->execution_mask;
+ }
spin_unlock(&ve->base.timeline.lock);
if (prio == I915_PRIORITY_INVALID)
goto out;
+ GEM_BUG_ON(!mask);
+
for (n = 0; READ_ONCE(ve->request) && n < ve->count; n++) {
struct intel_engine_cs *sibling = ve->siblings[n];
struct rb_node * const node = &ve->nodes[sibling->id].node;
struct rb_node **parent, *rb;
bool first;
+ if (unlikely(!(mask & BIT(n)))) {
+ if (!RB_EMPTY_NODE(node)) {
+ spin_lock(&sibling->timeline.lock);
+ rb_erase_cached(node,
+ &sibling->execlists.virtual);
+ RB_CLEAR_NODE(node);
+ spin_unlock(&sibling->timeline.lock);
+ }
+ continue;
+ }
+
spin_lock(&sibling->timeline.lock);
if (!RB_EMPTY_NODE(node)) {
@@ -3030,6 +3052,31 @@ static void virtual_submit_request(struct i915_request *request)
tasklet_schedule(&ve->base.execlists.tasklet);
}
+static struct ve_bond *
+virtual_find_bond(struct virtual_engine *ve, struct intel_engine_cs *master)
+{
+ int i;
+
+ for (i = 0; i < ve->nbond; i++) {
+ if (ve->bonds[i].master == master)
+ return &ve->bonds[i];
+ }
+
+ return NULL;
+}
+
+static void virtual_bond_execute(struct i915_sw_fence *fence, void *signal)
+{
+ struct i915_request *rq =
+ container_of(fence, struct i915_request, submit);
+ struct virtual_engine *ve = to_virtual_engine(rq->engine);
+ struct ve_bond *bond;
+
+ bond = virtual_find_bond(ve, to_request(signal)->engine);
+ if (bond) /* XXX serialise with rq->lock? */
+ rq->execution_mask &= bond->sibling_mask;
+}
+
struct intel_engine_cs *
intel_execlists_create_virtual(struct i915_gem_context *ctx,
struct intel_engine_cs **siblings,
@@ -3069,6 +3116,7 @@ intel_execlists_create_virtual(struct i915_gem_context *ctx,
ve->base.schedule = i915_schedule;
ve->base.submit_request = virtual_submit_request;
+ ve->base.bond_execute = virtual_bond_execute;
tasklet_init(&ve->base.execlists.tasklet,
virtual_submission_tasklet,
@@ -3121,6 +3169,37 @@ intel_execlists_create_virtual(struct i915_gem_context *ctx,
return ERR_PTR(err);
}
+int intel_virtual_engine_attach_bond(struct intel_engine_cs *engine,
+ struct intel_engine_cs *master,
+ unsigned int mask)
+{
+ struct virtual_engine *ve = to_virtual_engine(engine);
+ struct ve_bond *bond;
+
+ if (!mask || mask >> ve->count)
+ return -EINVAL;
+
+ bond = virtual_find_bond(ve, master);
+ if (bond) {
+ bond->sibling_mask |= mask;
+ return 0;
+ }
+
+ bond = krealloc(ve->bonds,
+ sizeof(*bond) * (ve->nbond + 1),
+ GFP_KERNEL);
+ if (!bond)
+ return -ENOMEM;
+
+ bond[ve->nbond].master = master;
+ bond[ve->nbond].sibling_mask = mask;
+
+ ve->bonds = bond;
+ ve->nbond++;
+
+ return 0;
+}
+
void intel_virtual_engine_put(struct intel_engine_cs *engine)
{
if (!engine)
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
index 12fcdc295192..514c3b519d3d 100644
--- a/drivers/gpu/drm/i915/intel_lrc.h
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -107,6 +107,9 @@ struct intel_engine_cs *
intel_execlists_create_virtual(struct i915_gem_context *ctx,
struct intel_engine_cs **siblings,
unsigned int count);
+int intel_virtual_engine_attach_bond(struct intel_engine_cs *engine,
+ struct intel_engine_cs *master,
+ unsigned int siblings);
void intel_virtual_engine_put(struct intel_engine_cs *engine);
#endif /* _INTEL_LRC_H_ */
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index f7c191c5ed0e..e114e2f2dc5e 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -499,6 +499,13 @@ struct intel_engine_cs {
*/
void (*submit_request)(struct i915_request *rq);
+ /*
+ * Called on signaling of a SUBMIT_FENCE, passing along the signaling
+ * request down to the bonded pairs.
+ */
+ void (*bond_execute)(struct i915_sw_fence *bond,
+ void *signal);
+
/*
* Call when the priority on a request has changed and it and its
* dependencies may need rescheduling. Note the request itself may
diff --git a/drivers/gpu/drm/i915/selftests/intel_lrc.c b/drivers/gpu/drm/i915/selftests/intel_lrc.c
index 530eb1411a01..6aa786f712f3 100644
--- a/drivers/gpu/drm/i915/selftests/intel_lrc.c
+++ b/drivers/gpu/drm/i915/selftests/intel_lrc.c
@@ -10,6 +10,7 @@
#include "../i915_selftest.h"
#include "igt_flush_test.h"
+#include "lib_sw_fence.h"
#include "mock_context.h"
@@ -760,6 +761,171 @@ static int live_virtual_engine(void *arg)
return err;
}
+static int bond_virtual_engine(struct drm_i915_private *i915,
+ unsigned int class,
+ struct intel_engine_cs **siblings,
+ unsigned int nsibling,
+ unsigned int flags)
+#define BOND_SCHEDULE BIT(0)
+{
+ struct intel_engine_cs *master;
+ struct i915_gem_context *ctx;
+ struct i915_request *rq[16];
+ enum intel_engine_id id;
+ unsigned long n;
+ int err;
+
+ GEM_BUG_ON(nsibling >= ARRAY_SIZE(rq) - 1);
+
+ ctx = kernel_context(i915);
+ if (!ctx)
+ return -ENOMEM;
+
+ err = 0;
+ rq[0] = ERR_PTR(-ENOMEM);
+ for_each_engine(master, i915, id) {
+ struct i915_sw_fence fence;
+
+ if (master->class == class)
+ continue;
+
+ rq[0] = i915_request_alloc(master, ctx);
+ if (IS_ERR(rq[0])) {
+ err = PTR_ERR(rq[0]);
+ goto out;
+ }
+
+ if (flags & BOND_SCHEDULE)
+ onstack_fence_init(&fence);
+
+ i915_request_get(rq[0]);
+ i915_request_add(rq[0]);
+
+ for (n = 0; n < nsibling; n++) {
+ struct intel_engine_cs *engine;
+
+ engine = intel_execlists_create_virtual(ctx,
+ siblings,
+ nsibling);
+ if (IS_ERR(engine)) {
+ err = PTR_ERR(engine);
+ goto out;
+ }
+
+ err = intel_virtual_engine_attach_bond(engine,
+ master,
+ BIT(n));
+ if (err) {
+ intel_virtual_engine_put(engine);
+ goto out;
+ }
+
+ rq[n + 1] = i915_request_alloc(engine, ctx);
+ if (IS_ERR(rq[n + 1])) {
+ err = PTR_ERR(rq[n + 1]);
+ intel_virtual_engine_put(engine);
+ goto out;
+ }
+ i915_request_get(rq[n + 1]);
+
+ err = i915_request_await_execution(rq[n + 1],
+ &rq[0]->fence,
+ engine->bond_execute);
+ i915_request_add(rq[n + 1]);
+ intel_virtual_engine_put(engine);
+ if (err < 0)
+ goto out;
+ }
+ rq[n + 1] = ERR_PTR(-EINVAL);
+
+ if (flags & BOND_SCHEDULE)
+ onstack_fence_fini(&fence);
+
+ for (n = 0; n < nsibling; n++) {
+ if (i915_request_wait(rq[n + 1],
+ I915_WAIT_LOCKED,
+ MAX_SCHEDULE_TIMEOUT) < 0) {
+ err = -EIO;
+ goto out;
+ }
+
+ if (rq[n + 1]->engine != siblings[n]) {
+ pr_err("Bonded request did not execute on target engine: expected %s, used %s; master was %s\n",
+ siblings[n]->name,
+ rq[n + 1]->engine->name,
+ rq[0]->engine->name);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ for (n = 0; !IS_ERR(rq[n]); n++)
+ i915_request_put(rq[n]);
+ rq[0] = ERR_PTR(-ENOMEM);
+ }
+
+out:
+ for (n = 0; !IS_ERR(rq[n]); n++)
+ i915_request_put(rq[n]);
+ if (igt_flush_test(i915, I915_WAIT_LOCKED))
+ err = -EIO;
+
+ kernel_context_close(ctx);
+ return err;
+}
+
+static int live_virtual_bond(void *arg)
+{
+ static const struct phase {
+ const char *name;
+ unsigned int flags;
+ } phases[] = {
+ { "", 0 },
+ { "schedule", BOND_SCHEDULE },
+ { },
+ };
+ struct drm_i915_private *i915 = arg;
+ struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1];
+ unsigned int class, inst;
+ int err = 0;
+
+ if (USES_GUC_SUBMISSION(i915))
+ return 0;
+
+ mutex_lock(&i915->drm.struct_mutex);
+
+ for (class = 0; class <= MAX_ENGINE_CLASS; class++) {
+ const struct phase *p;
+ int nsibling;
+
+ nsibling = 0;
+ for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) {
+ if (!i915->engine_class[class][inst])
+ break;
+
+ GEM_BUG_ON(nsibling == ARRAY_SIZE(siblings));
+ siblings[nsibling++] = i915->engine_class[class][inst];
+ }
+ if (nsibling < 2)
+ continue;
+
+ for (p = phases; p->name; p++) {
+ err = bond_virtual_engine(i915,
+ class, siblings, nsibling,
+ p->flags);
+ if (err) {
+ pr_err("%s(%s): failed class=%d, nsibling=%d, err=%d\n",
+ __func__, p->name, class, nsibling, err);
+ goto out_unlock;
+ }
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&i915->drm.struct_mutex);
+ return err;
+}
+
int intel_execlists_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
@@ -768,6 +934,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915)
SUBTEST(live_late_preempt),
SUBTEST(live_preempt_hang),
SUBTEST(live_virtual_engine),
+ SUBTEST(live_virtual_bond),
};
if (!HAS_EXECLISTS(i915))
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 6dd2a89410e8..6c927fcaabec 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -1526,6 +1526,7 @@ struct drm_i915_gem_context_param {
*
* Extensions:
* i915_context_engines_load_balance (I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE)
+ * i915_context_engines_bond (I915_CONTEXT_ENGINES_EXT_BOND)
*/
#define I915_CONTEXT_PARAM_ENGINES 0x7
@@ -1555,9 +1556,24 @@ struct i915_context_engines_load_balance {
__u64 mbz[4]; /* reserved for future use; must be zero */
};
+/*
+ * i915_context_engines_bond:
+ *
+ */
+struct i915_context_engines_bond {
+ struct i915_user_extension base;
+
+ __u32 master_class;
+ __u32 master_instance;
+
+ __u32 sibling_mask;
+ __u32 flags; /* all undefined flags must be zero */
+};
+
struct i915_context_param_engines {
__u64 extensions;
#define I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE 0
+#define I915_CONTEXT_ENGINES_EXT_BOND 1
struct {
__u32 class; /* see enum drm_i915_gem_engine_class */
--
2.19.0
More information about the Intel-gfx-trybot
mailing list