From patchwork Fri Jan 18 14:01:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 10770245 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 05EC36C5 for ; Fri, 18 Jan 2019 14:02:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E89762E694 for ; Fri, 18 Jan 2019 14:02:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E6A432E6F5; Fri, 18 Jan 2019 14:02:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 26FF92E715 for ; Fri, 18 Jan 2019 14:02:07 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id E875C6F7AD; Fri, 18 Jan 2019 14:01:59 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from fireflyinternet.com (mail.fireflyinternet.com [109.228.58.192]) by gabe.freedesktop.org (Postfix) with ESMTPS id 310126F7AF for ; Fri, 18 Jan 2019 14:01:51 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.65.138; Received: from haswell.alporthouse.com (unverified [78.156.65.138]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 15267264-1500050 for multiple; Fri, 18 Jan 2019 14:01:23 +0000 From: Chris Wilson To: intel-gfx@lists.freedesktop.org Date: Fri, 18 Jan 2019 14:01:06 +0000 Message-Id: <20190118140109.25261-36-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190118140109.25261-1-chris@chris-wilson.co.uk> References: <20190118140109.25261-1-chris@chris-wilson.co.uk> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH 35/38] drm/i915: Allow a context to define its set of engines X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP 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. The I915_EXEC_DEFAULT slot is left empty, and invalid for use by execbuf. It's use will be revealed in the next patch. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_gem_context.c | 158 +++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_context.h | 4 + drivers/gpu/drm/i915/i915_gem_execbuffer.c | 22 ++- include/uapi/drm/i915_drm.h | 30 ++++ 4 files changed, 208 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index e28be242399d..701af326120d 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -225,6 +225,8 @@ static void i915_gem_context_free(struct i915_gem_context *ctx) ce->ops->destroy(ce); } + kfree(ctx->engines); + if (ctx->timeline) i915_timeline_put(ctx->timeline); @@ -907,6 +909,155 @@ static int set_ppgtt(struct i915_gem_context *ctx, u32 id) return err; } +struct set_engines { + struct i915_gem_context *ctx; + struct intel_engine_cs **engines; + unsigned int nengine; +}; + +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; + struct set_engines set = { .ctx = ctx }; + u64 size, extensions; + unsigned int n; + int err; + + user = u64_to_user_ptr(args->value); + size = args->size; + if (!size) + goto out; + + if (size < sizeof(*user)) + return -EINVAL; + + size -= sizeof(*user); + if (size % sizeof(*user->class_instance)) + return -EINVAL; + + set.nengine = size / sizeof(*user->class_instance); + if (set.nengine == 0 || set.nengine > I915_EXEC_RING_MASK) + return -EINVAL; + + set.engines = kmalloc_array(set.nengine, + sizeof(*set.engines), + GFP_KERNEL); + if (!set.engines) + return -ENOMEM; + + for (n = 0; n < set.nengine; n++) { + u16 class, inst; + + if (get_user(class, &user->class_instance[n].engine_class) || + get_user(inst, &user->class_instance[n].engine_instance)) { + kfree(set.engines); + return -EFAULT; + } + + if (class == I915_ENGINE_CLASS_INVALID && + inst == I915_ENGINE_CLASS_INVALID_NONE) { + set.engines[n] = NULL; + continue; + } + + set.engines[n] = + intel_engine_lookup_user(ctx->i915, class, inst); + if (!set.engines[n]) { + kfree(set.engines); + return -ENOENT; + } + } + + 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) { + kfree(set.engines); + return err; + } + +out: + mutex_lock(&ctx->i915->drm.struct_mutex); + kfree(ctx->engines); + ctx->engines = set.engines; + ctx->nengine = set.nengine; + mutex_unlock(&ctx->i915->drm.struct_mutex); + + return 0; +} + +static int get_engines(struct i915_gem_context *ctx, + struct drm_i915_gem_context_param *args) +{ + struct i915_context_param_engines *local; + unsigned int n, count, size; + int err; + +restart: + count = READ_ONCE(ctx->nengine); + if (count > (INT_MAX - sizeof(*local)) / sizeof(*local->class_instance)) + return -ENOMEM; /* unrepresentable! */ + + size = sizeof(*local) + count * sizeof(*local->class_instance); + if (!args->size) { + args->size = size; + return 0; + } + if (args->size < size) + return -EINVAL; + + local = kmalloc(size, GFP_KERNEL); + if (!local) + return -ENOMEM; + + if (mutex_lock_interruptible(&ctx->i915->drm.struct_mutex)) { + err = -EINTR; + goto err; + } + + if (READ_ONCE(ctx->nengine) != count) { + mutex_unlock(&ctx->i915->drm.struct_mutex); + kfree(local); + goto restart; + } + + local->extensions = 0; + for (n = 0; n < count; n++) { + if (ctx->engines[n]) { + local->class_instance[n].engine_class = + ctx->engines[n]->uabi_class; + local->class_instance[n].engine_instance = + ctx->engines[n]->instance; + } else { + local->class_instance[n].engine_class = + I915_ENGINE_CLASS_INVALID; + local->class_instance[n].engine_instance = + I915_ENGINE_CLASS_INVALID_NONE; + } + } + + mutex_unlock(&ctx->i915->drm.struct_mutex); + + if (copy_to_user(u64_to_user_ptr(args->value), local, size)) { + err = -EFAULT; + goto err; + } + + args->size = size; + return 0; + +err: + kfree(local); + return err; +} + static int ctx_setparam(struct i915_gem_context *ctx, const struct drm_i915_gem_context_param *args) { @@ -971,6 +1122,10 @@ static int ctx_setparam(struct i915_gem_context *ctx, ret = set_ppgtt(ctx, lower_32_bits(args->value)); break; + case I915_CONTEXT_PARAM_ENGINES: + ret = set_engines(ctx, args); + break; + case I915_CONTEXT_PARAM_BAN_PERIOD: default: ret = -EINVAL; @@ -1121,6 +1276,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, case I915_CONTEXT_PARAM_VM: ret = get_ppgtt(ctx, &args->value); break; + case I915_CONTEXT_PARAM_ENGINES: + ret = get_engines(ctx, args); + break; default: ret = -EINVAL; break; diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h index b3a840747330..635f693994c4 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.h +++ b/drivers/gpu/drm/i915/i915_gem_context.h @@ -67,6 +67,8 @@ struct i915_gem_context { /** file_priv: owning file descriptor */ struct drm_i915_file_private *file_priv; + struct intel_engine_cs **engines; + struct i915_timeline *timeline; /** @@ -135,6 +137,8 @@ struct i915_gem_context { #define CONTEXT_CLOSED 1 #define CONTEXT_FORCE_SINGLE_SUBMISSION 2 + unsigned int nengine; + /** * @hw_id: - unique identifier for the context * diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index e63c179f3b3d..17187519e8f3 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -2016,13 +2016,23 @@ static const enum intel_engine_id user_ring_map[I915_USER_RINGS + 1] = { }; static struct intel_engine_cs * -eb_select_engine(struct drm_i915_private *dev_priv, +eb_select_engine(struct i915_execbuffer *eb, struct drm_file *file, struct drm_i915_gem_execbuffer2 *args) { unsigned int user_ring_id = args->flags & I915_EXEC_RING_MASK; struct intel_engine_cs *engine; + if (eb->ctx->engines) { + if (user_ring_id >= eb->ctx->nengine) { + DRM_DEBUG("execbuf with unknown ring: %u\n", + user_ring_id); + return NULL; + } + + return eb->ctx->engines[user_ring_id]; + } + if (user_ring_id > I915_USER_RINGS) { DRM_DEBUG("execbuf with unknown ring: %u\n", user_ring_id); return NULL; @@ -2035,11 +2045,11 @@ eb_select_engine(struct drm_i915_private *dev_priv, return NULL; } - if (user_ring_id == I915_EXEC_BSD && HAS_BSD2(dev_priv)) { + if (user_ring_id == I915_EXEC_BSD && HAS_BSD2(eb->i915)) { unsigned int bsd_idx = args->flags & I915_EXEC_BSD_MASK; if (bsd_idx == I915_EXEC_BSD_DEFAULT) { - bsd_idx = gen8_dispatch_bsd_engine(dev_priv, file); + bsd_idx = gen8_dispatch_bsd_engine(eb->i915, file); } else if (bsd_idx >= I915_EXEC_BSD_RING1 && bsd_idx <= I915_EXEC_BSD_RING2) { bsd_idx >>= I915_EXEC_BSD_SHIFT; @@ -2050,9 +2060,9 @@ eb_select_engine(struct drm_i915_private *dev_priv, return NULL; } - engine = dev_priv->engine[_VCS(bsd_idx)]; + engine = eb->i915->engine[_VCS(bsd_idx)]; } else { - engine = dev_priv->engine[user_ring_map[user_ring_id]]; + engine = eb->i915->engine[user_ring_map[user_ring_id]]; } if (!engine) { @@ -2262,7 +2272,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, if (unlikely(err)) goto err_destroy; - eb.engine = eb_select_engine(eb.i915, file, args); + eb.engine = eb_select_engine(&eb, file, args); if (!eb.engine) { err = -EINVAL; goto err_engine; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 9db459b19d4e..84f42317de92 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -122,6 +122,8 @@ enum drm_i915_gem_engine_class { I915_ENGINE_CLASS_INVALID = -1 }; +#define I915_ENGINE_CLASS_INVALID_NONE -1 + /** * DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915 * @@ -1464,9 +1466,37 @@ struct drm_i915_gem_context_param { #define I915_CONTEXT_DEFAULT_PRIORITY 0 #define I915_CONTEXT_MIN_USER_PRIORITY -1023 /* inclusive */ #define I915_CONTEXT_PARAM_VM 0x7 + +/* + * I915_CONTEXT_PARAM_ENGINES: + * + * Bind this context to operate on this subset of available engines. Henceforth, + * the I915_EXEC_RING selector for DRM_IOCTL_I915_GEM_EXECBUFFER2 operates as + * an index into this array of engines; I915_EXEC_DEFAULT selecting engine[0] + * and upwards. Slots 0...N are filled in using the specified (class, instance). + * Use (I915_ENGINE_CLASS_INVALID, I915_ENGINE_CLASS_INVALID_NONE) to specify + * a gap in the array that can be filled in later, e.g. by a virtual engine + * used for load balancing. + * + * Setting the number of engines bound to the context to 0, by passing a zero + * sized argument, will revert back to default settings. + * + * See struct i915_context_param_engines. + */ +#define I915_CONTEXT_PARAM_ENGINES 0x8 + __u64 value; }; +struct i915_context_param_engines { + __u64 extensions; /* linked chain of extension blocks, 0 terminates */ + + struct { + __u16 engine_class; /* see enum drm_i915_gem_engine_class */ + __u16 engine_instance; + } class_instance[0]; +}; + struct drm_i915_gem_context_create_ext_setparam { #define I915_CONTEXT_CREATE_EXT_SETPARAM 0 struct i915_user_extension base;