[Intel-gfx] [PATCH 12/18] drm/i915: Allow userspace to clone contexts on creation
Chris Wilson
chris at chris-wilson.co.uk
Thu Mar 21 14:38:33 UTC 2019
Quoting Tvrtko Ursulin (2019-03-20 13:13:45)
>
> On 19/03/2019 11:57, Chris Wilson wrote:
> > A usecase arose out of handling context recovery in mesa, whereby they
> > wish to recreate a context with fresh logical state but preserving all
> > other details of the original. Currently, they create a new context and
> > iterate over which bits they want to copy across, but it would much more
> > convenient if they were able to just pass in a target context to clone
> > during creation. This essentially extends the setparam during creation
> > to pull the details from a target context instead of the user supplied
> > parameters.
> >
> > Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> > ---
> > drivers/gpu/drm/i915/i915_gem_context.c | 154 ++++++++++++++++++++++++
> > include/uapi/drm/i915_drm.h | 14 +++
> > 2 files changed, 168 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
> > index fc1f64e19507..f36648329074 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_context.c
> > +++ b/drivers/gpu/drm/i915/i915_gem_context.c
> > @@ -1500,8 +1500,162 @@ static int create_setparam(struct i915_user_extension __user *ext, void *data)
> > return ctx_setparam(arg->ctx, &local.param);
> > }
> >
> > +static int clone_flags(struct i915_gem_context *dst,
> > + struct i915_gem_context *src)
> > +{
> > + dst->user_flags = src->user_flags;
> > + return 0;
> > +}
> > +
> > +static int clone_schedattr(struct i915_gem_context *dst,
> > + struct i915_gem_context *src)
> > +{
> > + dst->sched = src->sched;
> > + return 0;
> > +}
> > +
> > +static int clone_sseu(struct i915_gem_context *dst,
> > + struct i915_gem_context *src)
> > +{
> > + const struct intel_sseu default_sseu =
> > + intel_device_default_sseu(dst->i915);
> > + struct intel_engine_cs *engine;
> > + enum intel_engine_id id;
> > +
> > + for_each_engine(engine, dst->i915, id) {
>
> Hm in the load balancing patch this needs to be extended so the veng ce
> is also handled here.
>
> Possibly even when adding engine map the loop needs to iterate the map
> and not for_each_engine?
One problem is that it is hard to match a veng in one context with
another context, there may even be several :|
And then in clone_engines, we create a fresh virtual engine. So a nasty
interoperation with clone_engines.
Bleugh.
> > + struct intel_context *ce;
> > + struct intel_sseu sseu;
> > +
> > + ce = intel_context_lookup(src, engine);
> > + if (!ce)
> > + continue;
> > +
> > + sseu = ce->sseu;
> > + if (!memcmp(&sseu, &default_sseu, sizeof(sseu)))
>
> Could memcmp against &ce->sseu directly and keep src_ce and dst_ce so
> you can copy over without a temporary copy on stack?
At one point, the locking favoured making a local sseu to avoid
overlapping locks. Hmm, sseu = ce->sseu could still tear. Pedantically
that copy should be locked.
> > + continue;
> > +
> > + ce = intel_context_pin_lock(dst, engine);
> > + if (IS_ERR(ce))
> > + return PTR_ERR(ce);
> > +
> > + ce->sseu = sseu;
> > + intel_context_pin_unlock(ce);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int clone_timeline(struct i915_gem_context *dst,
> > + struct i915_gem_context *src)
> > +{
> > + if (src->timeline) {
> > + GEM_BUG_ON(src->timeline == dst->timeline);
> > +
> > + if (dst->timeline)
> > + i915_timeline_put(dst->timeline);
> > + dst->timeline = i915_timeline_get(src->timeline);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int clone_vm(struct i915_gem_context *dst,
> > + struct i915_gem_context *src)
> > +{
> > + struct i915_hw_ppgtt *ppgtt;
> > +
> > + rcu_read_lock();
> > + do {
> > + ppgtt = READ_ONCE(src->ppgtt);
> > + if (!ppgtt)
> > + break;
> > +
> > + if (!kref_get_unless_zero(&ppgtt->ref))
> > + continue;
> > +
> > + /*
> > + * This ppgtt may have be reallocated between
> > + * the read and the kref, and reassigned to a third
> > + * context. In order to avoid inadvertent sharing
> > + * of this ppgtt with that third context (and not
> > + * src), we have to confirm that we have the same
> > + * ppgtt after passing through the strong memory
> > + * barrier implied by a successful
> > + * kref_get_unless_zero().
> > + *
> > + * Once we have acquired the current ppgtt of src,
> > + * we no longer care if it is released from src, as
> > + * it cannot be reallocated elsewhere.
> > + */
> > +
> > + if (ppgtt == READ_ONCE(src->ppgtt))
> > + break;
> > +
> > + i915_ppgtt_put(ppgtt);
> > + } while (1);
> > + rcu_read_unlock();
>
> I still have the same problem. What if you added here:
>
> GEM_BUG_ON(ppgtt != READ_ONCE(src->ppgtt));
>
> Could it trigger? If so what is the point in the last check in the loop
> above?
Yes, it can trigger, as there is no outer mutex guarding the assignment
of src->ppgtt with our read. And that is why the check has to exist --
because it can be reassigned during the first read and before we acquire
the kref, and so ppgtt may have been freed and then reallocated and
assigned to a new ctx during that interval. We don't care that
src->ppgtt gets updated after we have taken a copy, we care that ppgtt
may get reused on another ctx entirely.
-Chris
More information about the Intel-gfx
mailing list