On 11.09.2025 10:30, Steven Price wrote:
> On 10/09/2025 17:50, Boris Brezillon wrote:
> > On Wed, 10 Sep 2025 16:56:43 +0100
> > Steven Price <steven.pr...@arm.com> wrote:
> >
> >> On 10/09/2025 16:52, Boris Brezillon wrote:
> >>> On Wed, 10 Sep 2025 16:42:32 +0100
> >>> Steven Price <steven.pr...@arm.com> wrote:
> >>>
> >>>>> +int panfrost_jm_ctx_create(struct drm_file *file,
> >>>>> +                          struct drm_panfrost_jm_ctx_create *args)
> >>>>> +{
> >>>>> +       struct panfrost_file_priv *priv = file->driver_priv;
> >>>>> +       struct panfrost_device *pfdev = priv->pfdev;
> >>>>> +       enum drm_sched_priority sched_prio;
> >>>>> +       struct panfrost_jm_ctx *jm_ctx;
> >>>>> +
> >>>>> +       int ret;
> >>>>> +
> >>>>> +       jm_ctx = kzalloc(sizeof(*jm_ctx), GFP_KERNEL);
> >>>>> +       if (!jm_ctx)
> >>>>> +               return -ENOMEM;
> >>>>> +
> >>>>> +       kref_init(&jm_ctx->refcnt);
> >>>>> +
> >>>>> +       /* Same priority for all JS within a single context */
> >>>>> +       jm_ctx->config = JS_CONFIG_THREAD_PRI(args->priority);
> >>>>> +
> >>>>> +       ret = jm_ctx_prio_to_drm_sched_prio(file, args->priority, 
> >>>>> &sched_prio);
> >>>>> +       if (ret)
> >>>>> +               goto err_put_jm_ctx;
> >>>>> +
> >>>>> +       for (u32 i = 0; i < NUM_JOB_SLOTS - 1; i++) {
> >>>>> +               struct drm_gpu_scheduler *sched = 
> >>>>> &pfdev->js->queue[i].sched;
> >>>>> +               struct panfrost_js_ctx *js_ctx = &jm_ctx->slots[i];
> >>>>> +
> >>>>> +               ret = drm_sched_entity_init(&js_ctx->sched_entity, 
> >>>>> sched_prio,
> >>>>> +                                           &sched, 1, NULL);
> >>>>> +               if (ret)
> >>>>> +                       goto err_put_jm_ctx;
> >>>>> +
> >>>>> +               js_ctx->enabled = true;
> >>>>> +       }
> >>>>> +
> >>>>> +       ret = xa_alloc(&priv->jm_ctxs, &args->handle, jm_ctx,
> >>>>> +                      XA_LIMIT(0, MAX_JM_CTX_PER_FILE), GFP_KERNEL);
> >>>>> +       if (ret)
> >>>>> +               goto err_put_jm_ctx;
> >>>>
> >>>> On error here we just jump down and call panfrost_jm_ctx_put() which
> >>>> will free jm_ctx but won't destroy any of the drm_sched_entities. There
> >>>> seems to be something a bit off with the lifetime management here.
> >>>>
> >>>> Should panfrost_jm_ctx_release() be responsible for tearing down the
> >>>> context, and panfrost_jm_ctx_destroy() be nothing more than dropping the
> >>>> reference?
> >>>
> >>> The idea was to kill/cancel any pending jobs as soon as userspace
> >>> releases the context, like we were doing previously when the FD was
> >>> closed. If we defer this ctx teardown to the release() function, we're
> >>> basically waiting for all jobs to complete, which:
> >>>
> >>> 1. doesn't encourage userspace to have proper control over the contexts
> >>>    lifetime
> >>> 2. might use GPU/mem resources to execute jobs no one cares about
> >>>    anymore
> >>
> >> Ah, good point - yes killing the jobs in panfrost_jm_ctx_destroy() makes
> >> sense. But we still need to ensure the clean-up happens in the other
> >> paths ;)
> >>
> >> So panfrost_jm_ctx_destroy() should keep the killing jobs part, butthe
> >> drm scheduler entity cleanup should be moved.
> >
> > The thing is, we need to call drm_sched_entity_fini() if we want all
> > pending jobs that were not queued to the HW yet to be cancelled
> > (_fini() calls _flush() + _kill()). This has to happen before we cancel
> > the jobs at the JM level, otherwise drm_sched might pass us new jobs
> > while we're trying to get rid of the currently running ones. Once we've
> > done that, there's basically nothing left to defer, except the kfree().
>
> Ok, I guess that makes sense.
>
> In which case panfrost_jm_ctx_create() just needs fixing to fully tear
> down the context in the event the xa_alloc() fails. Although that makes
> me wonder whether the reference counting on the JM context really
> achieves anything. Are we ever expecting the context to live past the
> panfrost_jm_ctx_destroy() call?

We still need reference counting, otherwise there would be a racy window between
the submission and context destruction ioctls, in which a context that has just
been released is still owned by a newly created job leading to UAF.

> Indeed is it even possible to have any in-flight jobs after
> drm_sched_entity_destroy() has returned?

My understanding of drm_sched_entity_destroy() is that, after it returns,
no jobs can be in-flight any more and the entity is rendered unusable by
any new jobs. This can lead to the unpleasant situation in which a thread
tries to submit a new job and gets a context reference right before another
thread takes precedence and destroys it, causing the scheduler entities to
be unusable.

Then drm_sched_job_init() would contain a reference to an invalid entity,
which further down the line would cause drm_sched_entity_push_job() to report
a DRM_ERROR warning that te entity is stopped, which should never happen,
because drm_sched_entity_push_job() must always suceed.

> Once all the sched entities have been destroyed there doesn't really
> seem to be anything left in struct panfrost_jm_ctx.

We've thought of a new approach whereby a context would be flagged as destroyed
inside panfrost_jm_ctx_destroy(), destruction of scheduler entities done at 
context
release time and then cancelling new jobs that had been queued after context
destruction in the .run_job scheduler function if they notice the context
is so flagged.

> Thanks,
> Steve

Cheers,
Adrian Larumbe

Reply via email to