Re: [PATCH v2 18/27] media: media-entity: Support context in pipeline_start
John Cox <[email protected]>
| Newsgroups | org.kernel.vger.linux-media,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAFyCYyNgKx27CnR36PufqzayS7BZxjRzay2eDucYPri=6aY=GA@mail.gmail.com> |
> 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 <[email protected]> > --- > 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 > >