[Intel-gfx] [PATCH] drm/i915: Reduce context HW ID lifetime
Chris Wilson
chris at chris-wilson.co.uk
Fri Aug 31 12:36:56 UTC 2018
Quoting Tvrtko Ursulin (2018-08-30 17:23:43)
>
> On 30/08/2018 11:24, Chris Wilson wrote:
> > +static int steal_hw_id(struct drm_i915_private *i915)
> > +{
> > + struct i915_gem_context *ctx, *cn;
> > + LIST_HEAD(pinned);
> > + int id = -ENOSPC;
> > +
> > + lockdep_assert_held(&i915->contexts.mutex);
> > +
> > + list_for_each_entry_safe(ctx, cn,
> > + &i915->contexts.hw_id_list, hw_id_link) {
> > + if (atomic_read(&ctx->pin_hw_id)) {
> > + list_move_tail(&ctx->hw_id_link, &pinned);
> > + continue;
> > + }
> > +
> > + GEM_BUG_ON(!ctx->hw_id); /* perma-pinned kernel context */
> > + list_del_init(&ctx->hw_id_link);
> > + id = ctx->hw_id;
> > + break;
> > + }
> > +
> > + list_splice_tail(&pinned, &i915->contexts.hw_id_list);
>
> Put a comment what is this code doing please. Trying to create some sort
> of LRU order?
LRSearched. Same as the shrinker, and eviction code if you would also
review that ;)
>
> > + return id;
> > +}
> > +
> > +static int assign_hw_id(struct drm_i915_private *i915, unsigned int *out)
> > +{
> > + int ret;
> > +
> > + lockdep_assert_held(&i915->contexts.mutex);
> > +
> > + ret = new_hw_id(i915, GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
> > + if (unlikely(ret < 0)) {
> > + ret = steal_hw_id(i915);
> > + if (ret < 0) /* once again for the correct erro code */
>
> errno
>
> > + ret = new_hw_id(i915, GFP_KERNEL);
>
> Hmm.. shouldn't you try GFP_KERNEL before attempting to steal? Actually
> I think you should branch based on -ENOSPC (steal) vs -ENOMEM (retry
> with GFP_KERNEL). Which would actually mean something like:
I was applying the same strategy as we use elsewhere. Penalise any
driver cache before hitting reclaim.
I think that is fair from an application of soft backpressure point of
view. (Lack of backpressure is probably a sore point for many.)
> > - ret = ida_simple_get(&dev_priv->contexts.hw_ida,
> > - 0, max, GFP_KERNEL);
>
> Although now that I see this I am struggling not to say the change to
> try a lighter weight allocation strategy first (gfp may fail) needs to
> be split out to a separate patch.
Pardon? I appear to suddenly be hard of hearing.
The patch was all about the steal_hw_id().
> > - if (ret < 0) {
> > - /* Contexts are only released when no longer active.
> > - * Flush any pending retires to hopefully release some
> > - * stale contexts and try again.
> > - */
> > - i915_retire_requests(dev_priv);
> > - ret = ida_simple_get(&dev_priv->contexts.hw_ida,
> > - 0, max, GFP_KERNEL);
> > - if (ret < 0)
> > - return ret;
> > - }
> > -
> > - *out = ret;
> > - return 0;
> > -}
> > -
> > static u32 default_desc_template(const struct drm_i915_private *i915,
> > const struct i915_hw_ppgtt *ppgtt)
> > {
> > @@ -276,12 +324,6 @@ __create_hw_context(struct drm_i915_private *dev_priv,
> > if (ctx == NULL)
> > return ERR_PTR(-ENOMEM);
> >
> > - ret = assign_hw_id(dev_priv, &ctx->hw_id);
> > - if (ret) {
> > - kfree(ctx);
> > - return ERR_PTR(ret);
> > - }
> > -
> > kref_init(&ctx->ref);
> > list_add_tail(&ctx->link, &dev_priv->contexts.list);
> > ctx->i915 = dev_priv;
> > @@ -295,6 +337,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
> >
> > INIT_RADIX_TREE(&ctx->handles_vma, GFP_KERNEL);
> > INIT_LIST_HEAD(&ctx->handles_list);
> > + INIT_LIST_HEAD(&ctx->hw_id_link);
> >
> > /* Default context will never have a file_priv */
> > ret = DEFAULT_CONTEXT_HANDLE;
> > @@ -421,15 +464,35 @@ i915_gem_context_create_gvt(struct drm_device *dev)
> > return ctx;
> > }
> >
> > +static void
> > +destroy_kernel_context(struct i915_gem_context **ctxp)
> > +{
> > + struct i915_gem_context *ctx;
> > +
> > + /* Keep the context ref so that we can free it immediately ourselves */
> > + ctx = i915_gem_context_get(fetch_and_zero(ctxp));
> > + GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
> > +
> > + context_close(ctx);
> > + i915_gem_context_free(ctx);
> > +}
> > +
> > struct i915_gem_context *
> > i915_gem_context_create_kernel(struct drm_i915_private *i915, int prio)
> > {
> > struct i915_gem_context *ctx;
> > + int err;
> >
> > ctx = i915_gem_create_context(i915, NULL);
> > if (IS_ERR(ctx))
> > return ctx;
> >
> > + err = i915_gem_context_pin_hw_id(ctx);
> > + if (err) {
> > + destroy_kernel_context(&ctx);
> > + return ERR_PTR(err);
> > + }
> > +
> > i915_gem_context_clear_bannable(ctx);
> > ctx->sched.priority = prio;
> > ctx->ring_size = PAGE_SIZE;
> > @@ -439,17 +502,19 @@ i915_gem_context_create_kernel(struct drm_i915_private *i915, int prio)
> > return ctx;
> > }
> >
> > -static void
> > -destroy_kernel_context(struct i915_gem_context **ctxp)
> > +static void init_contexts(struct drm_i915_private *i915)
> > {
> > - struct i915_gem_context *ctx;
> > + mutex_init(&i915->contexts.mutex);
> > + INIT_LIST_HEAD(&i915->contexts.list);
> >
> > - /* Keep the context ref so that we can free it immediately ourselves */
> > - ctx = i915_gem_context_get(fetch_and_zero(ctxp));
> > - GEM_BUG_ON(!i915_gem_context_is_kernel(ctx));
> > + /* Using the simple ida interface, the max is limited by sizeof(int) */
> > + BUILD_BUG_ON(MAX_CONTEXT_HW_ID > INT_MAX);
> > + BUILD_BUG_ON(GEN11_MAX_CONTEXT_HW_ID > INT_MAX);
> > + ida_init(&i915->contexts.hw_ida);
> > + INIT_LIST_HEAD(&i915->contexts.hw_id_list);
> >
> > - context_close(ctx);
> > - i915_gem_context_free(ctx);
> > + INIT_WORK(&i915->contexts.free_work, contexts_free_worker);
> > + init_llist_head(&i915->contexts.free_list);
>
> ugh diff.. :) looks like pure movement from perspective of
> destroy_kernel_context.
>
> > }
> >
> > static bool needs_preempt_context(struct drm_i915_private *i915)
> > @@ -470,14 +535,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
> > if (ret)
> > return ret;
> >
> > - INIT_LIST_HEAD(&dev_priv->contexts.list);
> > - INIT_WORK(&dev_priv->contexts.free_work, contexts_free_worker);
> > - init_llist_head(&dev_priv->contexts.free_list);
> > -
> > - /* Using the simple ida interface, the max is limited by sizeof(int) */
> > - BUILD_BUG_ON(MAX_CONTEXT_HW_ID > INT_MAX);
> > - BUILD_BUG_ON(GEN11_MAX_CONTEXT_HW_ID > INT_MAX);
> > - ida_init(&dev_priv->contexts.hw_ida);
> > + init_contexts(dev_priv);
> >
> > /* lowest priority; idle task */
> > ctx = i915_gem_context_create_kernel(dev_priv, I915_PRIORITY_MIN);
> > @@ -490,6 +548,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
> > * all user contexts will have non-zero hw_id.
> > */
> > GEM_BUG_ON(ctx->hw_id);
> > + GEM_BUG_ON(!atomic_read(&ctx->pin_hw_id));
>
> /* Kernel context is perma-pinned */
>
> > dev_priv->kernel_context = ctx;
> >
> > /* highest priority; preempting task */
> > @@ -527,6 +586,7 @@ void i915_gem_contexts_fini(struct drm_i915_private *i915)
> > destroy_kernel_context(&i915->kernel_context);
> >
> > /* Must free all deferred contexts (via flush_workqueue) first */
> > + GEM_BUG_ON(!list_empty(&i915->contexts.hw_id_list));
> > ida_destroy(&i915->contexts.hw_ida);
> > }
> >
> > @@ -932,6 +992,33 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev,
> > return ret;
> > }
> >
> > +int __i915_gem_context_pin_hw_id(struct i915_gem_context *ctx)
> > +{
> > + struct drm_i915_private *i915 = ctx->i915;
> > + int err = 0;
> > +
> > + mutex_lock(&i915->contexts.mutex);
> > +
> > + GEM_BUG_ON(i915_gem_context_is_closed(ctx));
> > +
> > + if (list_empty(&ctx->hw_id_link)) {
> > + GEM_BUG_ON(atomic_read(&ctx->pin_hw_id));
> > +
> > + err = assign_hw_id(i915, &ctx->hw_id);
> > + if (err)
> > + goto out_unlock;
> > +
> > + list_add_tail(&ctx->hw_id_link, &i915->contexts.hw_id_list);
> > + }
> > +
> > + GEM_BUG_ON(atomic_read(&ctx->pin_hw_id) == ~0u);
> > + atomic_inc(&ctx->pin_hw_id);
> > +
> > +out_unlock:
> > + mutex_unlock(&i915->contexts.mutex);
> > + return err;
> > +}
> > +
> > #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> > #include "selftests/mock_context.c"
> > #include "selftests/i915_gem_context.c"
> > diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h
> > index 851dad6decd7..c73ac614f58c 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_context.h
> > +++ b/drivers/gpu/drm/i915/i915_gem_context.h
> > @@ -136,6 +136,8 @@ struct i915_gem_context {
> > * id for the lifetime of the context.
> > */
> > unsigned int hw_id;
> > + atomic_t pin_hw_id;
>
> I think now we need short comments describing the difference between the
> two.
One is 32bits unsigned, unserialised. The other is 32bits signed, and
very loosely serialised :)
> > + struct list_head hw_id_link;
>
> And for this one.
[snip]
> So in essence there will be a little bit more cost when pinning in the
> normal case, or a bit bit more in the stealing/pathological case, but as
> long as we stay below over-subscription the cost is only on first pin.
> No complaints there. Debug also won't be confusing in the normal case
> since numbers will be stable.
Yup. Nice addition to the changelog, thanks.
> Does it have any negative connotations in the world of OA is the
> question for Lionel?
Lionel kept promising me this was ok, that he/gputop was quite ready for
shorter lived ctx id, and reuse.
-Chris
More information about the Intel-gfx
mailing list