[PATCH v2 18/27] media: media-entity: Support context in pipeline_start

John Cox jc at kynesim.co.uk
Thu Aug 20 09:10:59 PDT 2026


> Add helpers to propagate an execution context when starting the media
> pipeline. Starting a pipeline with an execution context implies that
> when creating the streaming pipeline all the entities part of the
> pipeline shall have a valid context associated with the media context in
> use.
>
> When creating the media pipeline increase the device context use
> count to guarantee that during the streaming phase drivers will
> always operate with valid contexts.
>
> Add 'overrides' for all the video_device_pipeline_start() and
> media_pipeline_start() variants to support contexts.
>
> Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
> ---
>  drivers/media/mc/mc-entity.c       | 150 ++++++++++++++++++++++++++++++++-----
>  drivers/media/v4l2-core/v4l2-dev.c |  44 +++++++++++
>  include/media/media-entity.h       |  56 +++++++++++++-
>  include/media/v4l2-dev.h           |  57 ++++++++++++++
>  4 files changed, 287 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
> index 7bc276c725f974539ea06e3882d004b81be1de68..f421d6d74630bb96400d39d805c5db5d3d1ff913 100644
> --- a/drivers/media/mc/mc-entity.c
> +++ b/drivers/media/mc/mc-entity.c
> @@ -768,8 +768,50 @@ static int media_pipeline_populate(struct media_pipeline *pipe,
>         return ret;
>  }
>
> -__must_check int __media_pipeline_start(struct media_pad *origin,
> -                                       struct media_pipeline *pipe)
> +static int
> +media_pipeline_validate_context(struct media_device_context *mdev_context,
> +                               struct media_entity *entity,
> +                               struct media_pipeline_pad *ppad)
> +{
> +       struct media_entity_context *context;
> +
> +       if (!mdev_context)
> +               return 0;
> +
> +       /*
> +        * It's not mandatory for all entities in the pipeline to support
> +        * contexts.
> +        */
> +       if (!entity->ops || !entity->ops->alloc_context ||
> +           !entity->ops->destroy_context)
> +               return 0;
> +
> +       /*
> +        * But if they do they should be bound to the same media device context
> +        * as all other entities.
> +        *
> +        * media_device_get_entity_context increases the ref-counting of the
> +        * context. Store a reference in the ppad for later decreasing the
> +        * reference count when the pipeline is stopped.
> +        *
> +        * Fail validation if no context is associated with this media context
> +        * and be loud about that as userspace should be informed it has to
> +        * bind all entities of the pipeline in the same context.
> +        */
> +       context = media_device_get_entity_context(mdev_context, entity);
> +       if (WARN_ON(IS_ERR(context)))
> +               return -EPIPE;
> +
> +       media_entity_context_get(context);
I'm pretty sure this is a second unintended context_get as it is already done
by the media_device_get_entity_context() above.

Regards

John Cox

> +       ppad->context = context;
> +
> +       return 0;
> +}
> +
> +__must_check int
> +__media_pipeline_start_context(struct media_pad *origin,
> +                              struct media_pipeline *pipe,
> +                              struct media_device_context *mdev_context)
>  {
>         struct media_device *mdev = origin->graph_obj.mdev;
>         struct media_pipeline_pad *err_ppad;
> @@ -829,7 +871,15 @@ __must_check int __media_pipeline_start(struct media_pad *origin,
>                 }
>
>                 /*
> -                * 2. Validate all active links whose sink is the current pad.
> +                * 2. If we have a media context, ensure the entity has a device
> +                * context associated with it.
> +                */
> +               ret = media_pipeline_validate_context(mdev_context, entity, ppad);
> +               if (ret)
> +                       goto error;
> +
> +               /*
> +                * 3. Validate all active links whose sink is the current pad.
>                  * Validation of the source pads is performed in the context of
>                  * the connected sink pad to avoid duplicating checks.
>                  */
> @@ -875,7 +925,7 @@ __must_check int __media_pipeline_start(struct media_pad *origin,
>                 }
>
>                 /*
> -                * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
> +                * 4. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
>                  * ensure that it has either no link or an enabled link.
>                  */
>                 if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) &&
> @@ -905,6 +955,9 @@ __must_check int __media_pipeline_start(struct media_pad *origin,
>                 if (err_ppad == ppad)
>                         break;
>
> +               if (err_ppad->context)
> +                       media_entity_context_put(err_ppad->context);
> +
>                 err_ppad->pad->pipe = NULL;
>         }
>
> @@ -912,19 +965,35 @@ __must_check int __media_pipeline_start(struct media_pad *origin,
>
>         return ret;
>  }
> +EXPORT_SYMBOL_GPL(__media_pipeline_start_context);
> +
> +__must_check int __media_pipeline_start(struct media_pad *origin,
> +                                       struct media_pipeline *pipe)
> +{
> +       return __media_pipeline_start_context(origin, pipe, NULL);
> +}
>  EXPORT_SYMBOL_GPL(__media_pipeline_start);
>
> -__must_check int media_pipeline_start(struct media_pad *origin,
> -                                     struct media_pipeline *pipe)
> +__must_check int
> +media_pipeline_start_context(struct media_pad *origin,
> +                            struct media_pipeline *pipe,
> +                            struct media_device_context *context)
>  {
>         struct media_device *mdev = origin->graph_obj.mdev;
>         int ret;
>
>         mutex_lock(&mdev->graph_mutex);
> -       ret = __media_pipeline_start(origin, pipe);
> +       ret = __media_pipeline_start_context(origin, pipe, context);
>         mutex_unlock(&mdev->graph_mutex);
>         return ret;
>  }
> +EXPORT_SYMBOL_GPL(media_pipeline_start_context);
> +
> +__must_check int media_pipeline_start(struct media_pad *origin,
> +                                     struct media_pipeline *pipe)
> +{
> +       return media_pipeline_start_context(origin, pipe, NULL);
> +}
>  EXPORT_SYMBOL_GPL(media_pipeline_start);
>
>  void __media_pipeline_stop(struct media_pad *pad)
> @@ -942,8 +1011,11 @@ void __media_pipeline_stop(struct media_pad *pad)
>         if (--pipe->start_count)
>                 return;
>
> -       list_for_each_entry(ppad, &pipe->pads, list)
> +       list_for_each_entry(ppad, &pipe->pads, list) {
> +               if (ppad->context)
> +                       media_entity_context_put(ppad->context);
>                 ppad->pad->pipe = NULL;
> +       }
>
>         media_pipeline_cleanup(pipe);
>
> @@ -962,14 +1034,13 @@ void media_pipeline_stop(struct media_pad *pad)
>  }
>  EXPORT_SYMBOL_GPL(media_pipeline_stop);
>
> -__must_check int media_pipeline_alloc_start(struct media_pad *pad)
> +static struct media_pipeline *
> +media_pipeline_alloc(struct media_pad *pad)
>  {
>         struct media_device *mdev = pad->graph_obj.mdev;
> -       struct media_pipeline *new_pipe = NULL;
>         struct media_pipeline *pipe;
> -       int ret;
>
> -       mutex_lock(&mdev->graph_mutex);
> +       lockdep_assert_held(&mdev->graph_mutex);
>
>         /*
>          * Is the pad already part of a pipeline? If not, we need to allocate
> @@ -977,19 +1048,33 @@ __must_check int media_pipeline_alloc_start(struct media_pad *pad)
>          */
>         pipe = media_pad_pipeline(pad);
>         if (!pipe) {
> -               new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL);
> -               if (!new_pipe) {
> -                       ret = -ENOMEM;
> -                       goto out;
> -               }
> +               pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
> +               if (!pipe)
> +                       return ERR_PTR(-ENOMEM);
>
> -               pipe = new_pipe;
>                 pipe->allocated = true;
>         }
>
> +       return pipe;
> +}
> +
> +__must_check int media_pipeline_alloc_start(struct media_pad *pad)
> +{
> +       struct media_device *mdev = pad->graph_obj.mdev;
> +       struct media_pipeline *pipe;
> +       int ret;
> +
> +       mutex_lock(&mdev->graph_mutex);
> +
> +       pipe = media_pipeline_alloc(pad);
> +       if (IS_ERR(pipe)) {
> +               ret = PTR_ERR(pipe);
> +               goto out;
> +       }
> +
>         ret = __media_pipeline_start(pad, pipe);
>         if (ret)
> -               kfree(new_pipe);
> +               kfree(pipe);
>
>  out:
>         mutex_unlock(&mdev->graph_mutex);
> @@ -998,6 +1083,33 @@ __must_check int media_pipeline_alloc_start(struct media_pad *pad)
>  }
>  EXPORT_SYMBOL_GPL(media_pipeline_alloc_start);
>
> +__must_check
> +int media_pipeline_alloc_start_context(struct media_pad *pad,
> +                                      struct media_device_context *mdev_context)
> +{
> +       struct media_device *mdev = pad->graph_obj.mdev;
> +       struct media_pipeline *pipe;
> +       int ret;
> +
> +       mutex_lock(&mdev->graph_mutex);
> +
> +       pipe = media_pipeline_alloc(pad);
> +       if (IS_ERR(pipe)) {
> +               ret = PTR_ERR(pipe);
> +               goto out;
> +       }
> +
> +       ret = __media_pipeline_start_context(pad, pipe, mdev_context);
> +       if (ret)
> +               kfree(pipe);
> +
> +out:
> +       mutex_unlock(&mdev->graph_mutex);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(media_pipeline_alloc_start_context);
> +
>  struct media_pad *
>  __media_pipeline_pad_iter_next(struct media_pipeline *pipe,
>                                struct media_pipeline_pad_iter *iter,
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index fafd4209e2cab320a6e164d33e9a0f73784d22db..eacaa38cffe8847b66acdeebf2330a072b3be85f 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -1221,6 +1221,36 @@ __must_check int __video_device_pipeline_start(struct video_device *vdev,
>  }
>  EXPORT_SYMBOL_GPL(__video_device_pipeline_start);
>
> +__must_check int
> +video_device_context_pipeline_start(struct video_device_context *context,
> +                                   struct media_pipeline *pipe)
> +{
> +       struct video_device *vdev = context->vdev;
> +       struct media_entity *entity = &vdev->entity;
> +
> +       if (entity->num_pads != 1)
> +               return -ENODEV;
> +
> +       return media_pipeline_start_context(&entity->pads[0], pipe,
> +                                           context->base.mdev_context);
> +}
> +EXPORT_SYMBOL(video_device_context_pipeline_start);
> +
> +__must_check int
> +__video_device_context_pipeline_start(struct video_device_context *context,
> +                                     struct media_pipeline *pipe)
> +{
> +       struct video_device *vdev = context->vdev;
> +       struct media_entity *entity = &vdev->entity;
> +
> +       if (entity->num_pads != 1)
> +               return -ENODEV;
> +
> +       return __media_pipeline_start_context(&entity->pads[0], pipe,
> +                                             context->base.mdev_context);
> +}
> +EXPORT_SYMBOL(__video_device_context_pipeline_start);
> +
>  void video_device_pipeline_stop(struct video_device *vdev)
>  {
>         struct media_entity *entity = &vdev->entity;
> @@ -1254,6 +1284,20 @@ __must_check int video_device_pipeline_alloc_start(struct video_device *vdev)
>  }
>  EXPORT_SYMBOL_GPL(video_device_pipeline_alloc_start);
>
> +__must_check int
> +video_device_context_pipeline_alloc_start(struct video_device_context *context)
> +{
> +       struct video_device *vdev = context->vdev;
> +       struct media_entity *entity = &vdev->entity;
> +
> +       if (entity->num_pads != 1)
> +               return -ENODEV;
> +
> +       return media_pipeline_alloc_start_context(&entity->pads[0],
> +                                                 context->base.mdev_context);
> +}
> +EXPORT_SYMBOL_GPL(video_device_context_pipeline_alloc_start);
> +
>  struct media_pipeline *video_device_pipeline(struct video_device *vdev)
>  {
>         struct media_entity *entity = &vdev->entity;
> diff --git a/include/media/media-entity.h b/include/media/media-entity.h
> index 32298fe8a18c6ee3c1dbcff9ef869548904417a7..b60c311ab390beb6931fe8f2bbe8939e11cda452 100644
> --- a/include/media/media-entity.h
> +++ b/include/media/media-entity.h
> @@ -119,16 +119,20 @@ struct media_pipeline {
>   * @list:              Entry in the media_pad pads list
>   * @pipe:              The media_pipeline that the pad is part of
>   * @pad:               The media pad
> + * @context:           Reference to a video device or subdevice context
>   *
>   * This structure associate a pad with a media pipeline. Instances of
>   * media_pipeline_pad are created by media_pipeline_start() when it builds the
>   * pipeline, and stored in the &media_pad.pads list. media_pipeline_stop()
> - * removes the entries from the list and deletes them.
> + * removes the entries from the list and deletes them. The context field is
> + * populated only if a valid context has been associated with the pad.
>   */
> +struct media_entity_context;
>  struct media_pipeline_pad {
>         struct list_head list;
>         struct media_pipeline *pipe;
>         struct media_pad *pad;
> +       struct media_entity_context *context;
>  };
>
>  /**
> @@ -1212,6 +1216,39 @@ __must_check int media_pipeline_start(struct media_pad *origin,
>  __must_check int __media_pipeline_start(struct media_pad *origin,
>                                         struct media_pipeline *pipe);
>
> +/**
> + * media_pipeline_start_context - Mark a pipeline as streaming
> + * @origin: Starting pad
> + * @pipe: Media pipeline to be assigned to all pads in the pipeline.
> + * @context: The media device context the pipeline belongs to
> + *
> + * Mark all pads connected to a given pad through enabled links, either
> + * directly or indirectly, as streaming. The given pipeline object is assigned
> + * to every pad in the pipeline and stored in the media_pad pipe field.
> + *
> + * Calls to this function can be nested, in which case the same number of
> + * media_pipeline_stop() calls will be required to stop streaming. The
> + * pipeline pointer must be identical for all nested calls to
> + * media_pipeline_start().
> + */
> +__must_check int
> +media_pipeline_start_context(struct media_pad *origin,
> +                            struct media_pipeline *pipe,
> +                            struct media_device_context *context);
> +
> +/**
> + * __media_pipeline_start_context - Mark a pipeline as streaming
> + * @origin: Starting pad
> + * @pipe: Media pipeline to be assigned to all pads in the pipeline.
> + * @context: The media device context the pipeline belongs to
> + *
> + * ..note:: This is the non-locking version of media_pipeline_start_context()
> + */
> +__must_check int
> +__media_pipeline_start_context(struct media_pad *origin,
> +                              struct media_pipeline *pipe,
> +                              struct media_device_context *context);
> +
>  /**
>   * media_pipeline_stop - Mark a pipeline as not streaming
>   * @pad: Starting pad
> @@ -1318,6 +1355,23 @@ __media_pipeline_entity_iter_next(struct media_pipeline *pipe,
>   */
>  __must_check int media_pipeline_alloc_start(struct media_pad *pad);
>
> +/**
> + * media_pipeline_alloc_start_context - Mark a pipeline as streaming
> + * @pad: Starting pad
> + * @context: The media device context the pipeline belongs to
> + *
> + * media_pipeline_alloc_start_context() is similar to
> + * media_pipeline_start_context() but instead of working on a given pipeline the
> + * function will use an existing pipeline if the pad is already part of a
> + * pipeline, or allocate a new pipeline.
> + *
> + * Calls to media_pipeline_alloc_start_context() must be matched with
> + * media_pipeline_stop().
> + */
> +__must_check int
> +media_pipeline_alloc_start_context(struct media_pad *pad,
> +                                  struct media_device_context *context);
> +
>  /**
>   * media_devnode_create() - creates and initializes a device node interface
>   *
> diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
> index bab4b13b109362bec84d8d16440b6ea895206b60..93095df692e3628d4be003d25843fa744d6b20a4 100644
> --- a/include/media/v4l2-dev.h
> +++ b/include/media/v4l2-dev.h
> @@ -602,6 +602,48 @@ __must_check int video_device_pipeline_start(struct video_device *vdev,
>  __must_check int __video_device_pipeline_start(struct video_device *vdev,
>                                                struct media_pipeline *pipe);
>
> +/**
> + * video_device_context_pipeline_start - Mark a pipeline as streaming starting
> + *                                      from a video device context
> + * @context: The video device context that starts the streaming
> + * @pipe: Media pipeline to be assigned to all entities in the pipeline.
> + *
> + * ..note:: This is the multi-context version of video_device_pipeline_start()
> + *          Documentation of this function only describes specific aspects of
> + *          this version. Refer to the video_device_pipeline_start()
> + *          documentation for a complete reference.
> + *
> + * Validate that all entities connected to a video device through enabled links
> + * by ensuring that a context associated with the same media device context
> + * exists for them. Increase the reference counting of each of the contexts part
> + * of the pipeline to guarantee their lifetime is maintained as long as the
> + * pipeline is streaming.
> + *
> + * Context validation and refcounting of all entities that are part of a
> + * streaming pipeline ensures that device drivers can safely access device
> + * contexts in a media device context during streaming. References to contexts
> + * retried by a call to media_device_get_entity_context(), are guaranteed to be
> + * valid as long as the pipeline is streaming. Likewise, the media device
> + * context that contains the device contexts is guaranteed to be valid as long
> + * as the pipeline is streaming.
> + */
> +__must_check int
> +video_device_context_pipeline_start(struct video_device_context *context,
> +                                   struct media_pipeline *pipe);
> +
> +/**
> + * __video_device_context_pipeline_start - Mark a pipeline as streaming starting
> + *                                        from a video device context
> + * @context: The video device context that starts the streaming
> + * @pipe: Media pipeline to be assigned to all entities in the pipeline.
> + *
> + * ..note:: This is the non-locking version of
> + *         __video_device_context_pipeline_start()
> + */
> +__must_check int
> +__video_device_context_pipeline_start(struct video_device_context *context,
> +                                     struct media_pipeline *pipe);
> +
>  /**
>   * video_device_pipeline_stop - Mark a pipeline as not streaming
>   * @vdev: Starting video device
> @@ -646,6 +688,21 @@ void __video_device_pipeline_stop(struct video_device *vdev);
>   */
>  __must_check int video_device_pipeline_alloc_start(struct video_device *vdev);
>
> +/**
> + * video_device_context_pipeline_alloc_start - Mark a pipeline as streaming
> + * @context: The video device context that starts the streaming
> + *
> + * video_device_context_pipeline_alloc_start() is similar to
> + * video_device_context_pipeline_start() but instead of working on a given
> + * pipeline the function will use an existing pipeline if the video device is
> + * already part of a pipeline, or allocate a new pipeline.
> + *
> + * Calls to video_device_context_pipeline_alloc_start() must be matched with
> + * video_device_pipeline_stop().
> + */
> +__must_check int
> +video_device_context_pipeline_alloc_start(struct video_device_context *context);
> +
>  /**
>   * video_device_pipeline - Get the media pipeline a video device is part of
>   * @vdev: The video device
>
> --
> 2.49.0
>
>



More information about the linux-arm-kernel mailing list