[Intel-gfx] [PATCH 22/50] drm/i915: Allow a context to define its set of engines

Chris Wilson chris at chris-wilson.co.uk
Mon Apr 15 12:31:40 UTC 2019


Quoting Tvrtko Ursulin (2019-04-15 13:19:45)
> > +     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",
> 
> Standardize early on "%u:%u" for class:instance? Although in this 
> specific case I am not the debug is so useful. (There are many other 
> ways the ioctl can fail.) Same goes for the earlier one.

I'm quietly adopting yaml inside messages :)

It's the -EINVAL that are a nightmare. -EFAULT, -ENOMEM, other
specialised errno are easy to recognise, but -EINVAL is anything and
usually what devs hit in practice.

I can take out the DRM_DEBUG again -- I really don't like using dmesg
for user error messages :)

> > +                               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->i915->drm.struct_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->i915->drm.struct_mutex);
> 
> Hm so what is the new engine list mutex for?

I missed replacing this with engine_mutex. It used to be required for
execbuf serialisation, however that is fine now with the RCU lookup so
we only need mutex around the assignment and prolonged access.

> > +     INIT_RCU_WORK(&set.engines->rcu, free_engines_rcu);
> > +     queue_rcu_work(system_wq, &set.engines->rcu);
> > +
> > +     return 0;
> > +}
> > +
> > +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;
> > +
> > +     if (!i915_gem_context_user_engines(ctx)) {
> > +             args->size = 0;
> > +             goto unlock;
> > +     }
> > +
> > +     e = i915_gem_context_engine_list(ctx);
> > +     count = e->num_engines;
> > +
> > +     /* Be paranoid in case we have an impedance mismatch */
> > +     if (!check_struct_size(user, engines, count, &size)) {
> > +             err = -ENOMEM;
> > +             goto unlock;
> > +     }
> > +     if (unlikely(overflows_type(size, args->size))) {
> 
> I wouldn't bother with unlikely, none of the other failure modes does.

check_struct_size is unlikely, we can just push it into overflows_type I
guess.

> 
> > +             err = -ENOMEM;
> > +             goto unlock;
> 
> Or abuse a bit -E2BIG for the two?

No, the other choice here is -EINVAL.

> > +     }
> > +
> > +     if (!args->size) {
> > +             args->size = size;
> > +             goto unlock;
> > +     }
> > +
> > +     if (args->size < size) {
> > +             err = -EINVAL;
> > +             goto unlock;
> > +     }
> > +
> > +     user = u64_to_user_ptr(args->value);
> > +     if (!access_ok(user, size)) {
> > +             err = -EFAULT;
> > +             goto unlock;
> > +     }
> > +
> > +     if (put_user(0, &user->extensions)) {
> > +             err = -EFAULT;
> 
> Hm why?

Why an error for failing to write into the user struct? Or why are there
no extensions?

> > +             goto unlock;
> > +     }
> > +
> > +     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;
> > +             }
> 
> I would be tempted to put the invalid initializers into the else block.

I'd be tempted too, but I liked the look of the 3 stanzas :)

> > +
> > +             if (copy_to_user(&user->engines[n], &ci, sizeof(ci))) {
> 
> __copy_to_user after access_ok passed? Or there were some changes in 
> this area?

Just not expecting a hot ioctl, so I wasn't worried about a bit of
redundancy so chose not to argue why the "unsafe" variants are ok.

> > diff --git a/drivers/gpu/drm/i915/i915_gem_context_types.h b/drivers/gpu/drm/i915/i915_gem_context_types.h
> > index 33e5a0e36e75..5284b50a4751 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_context_types.h
> > +++ b/drivers/gpu/drm/i915/i915_gem_context_types.h
> > @@ -139,6 +139,14 @@ struct i915_gem_context {
> >   #define CONTEXT_BANNED                      0
> >   #define CONTEXT_CLOSED                      1
> >   #define CONTEXT_FORCE_SINGLE_SUBMISSION     2
> > +#define CONTEXT_USER_ENGINES         3
> > +
> > +     /**
> > +      * @num_engines: Number of user defined engines for this context
> > +      *
> > +      * See @engines for the elements.
> > +      */
> > +     unsigned int num_engines;
> 
> Where is this set or used?

Tasty leftovers.
-Chris


More information about the Intel-gfx mailing list