Re: [PATCH v3 5/9] media: vimc: Separate closing of stream and thread

From: Kieran Bingham
Date: Wed Sep 02 2020 - 06:13:18 EST


Hi Kaaira,

On 19/08/2020 19:04, Kaaira Gupta wrote:
> Make separate functions for stopping streaming of entities in path of a
> particular stream and stopping thread. This is needed to ensure that
> thread doesn't stop when one device stops streaming in case of multiple
> streams.
>
> Signed-off-by: Kaaira Gupta <kgupta@xxxxxxxxxxxxx>
> ---
> .../media/test-drivers/vimc/vimc-streamer.c | 82 ++++++++++++-------
> 1 file changed, 52 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/media/test-drivers/vimc/vimc-streamer.c b/drivers/media/test-drivers/vimc/vimc-streamer.c
> index cc40ecabe95a..6b5ea1537952 100644
> --- a/drivers/media/test-drivers/vimc/vimc-streamer.c
> +++ b/drivers/media/test-drivers/vimc/vimc-streamer.c
> @@ -13,29 +13,59 @@
> #include "vimc-streamer.h"
>
> /**
> - * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
> + * vimc_streamer_pipeline_terminate - Terminate the thread
> *
> - * @stream: the pointer to the stream structure with the pipeline to be
> - * disabled.
> + * @stream: the pointer to the stream structure
> *
> - * Calls s_stream to disable the stream in each entity of the pipeline
> + * Erases values of stream struct and terminates the thread

It would help if the function brief described it's purpose rather than
'what it does'. "Erases values of stream struct" is not helpful here, as
that's just a direct read of what happens in the code.

I'm guessing here, but how about:

"Tear down all resources belonging to the pipeline when there are no
longer any active streams being used. This includes stopping the active
processing thread"


But reading my text makes me worry about the ordering that might take
place. The thread should be stopped as soon as the last stream using it
is stopped. Presumably as the 'first thing' that happens to make sure
there is no concurrent processing while the stream is being disabled.

Hopefully there's no race condition ...


> *
> */
> static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
> {
> struct vimc_ent_device *ved;
> - struct v4l2_subdev *sd;
>
> while (stream->pipe_size) {
> stream->pipe_size--;
> ved = stream->ved_pipeline[stream->pipe_size];
> stream->ved_pipeline[stream->pipe_size] = NULL;
> + }
>
> - if (!is_media_entity_v4l2_subdev(ved->ent))
> - continue;
> + kthread_stop(stream->kthread);
> + stream->kthread = NULL;
> +}
>
> - sd = media_entity_to_v4l2_subdev(ved->ent);
> - v4l2_subdev_call(sd, video, s_stream, 0);
> +/**
> + * vimc_streamer_stream_terminate - Disable stream in all ved in stream
> + *
> + * @ved: pointer to the ved for which stream needs to be disabled
> + *
> + * Calls s_stream to disable the stream in each entity of the stream
> + *
> + */
> +static void vimc_streamer_stream_terminate(struct vimc_ent_device *ved)

I would call this vimc_streamer_stream_stop to match
vimc_streamer_stream_start() rather than terminate...

> +{
> + struct media_entity *entity = ved->ent;
> + struct video_device *vdev;
> + struct v4l2_subdev *sd;
> +
> + while (entity) {
> + if (is_media_entity_v4l2_subdev(ved->ent)) {
> + sd = media_entity_to_v4l2_subdev(ved->ent);
> + v4l2_subdev_call(sd, video, s_stream, 0);
> + }
> + entity = vimc_get_source_entity(ved->ent);
> + if (!entity)
> + break;
> +
> + if (is_media_entity_v4l2_subdev(entity)) {
> + sd = media_entity_to_v4l2_subdev(entity);
> + ved = v4l2_get_subdevdata(sd);
> + } else {
> + vdev = container_of(entity,
> + struct video_device,
> + entity);
> + ved = video_get_drvdata(vdev);
> + }

It looks like this is walking back through the linked graph, calling
s_stream(0) right?

I wonder if struct vimc_ent_device should have a pointer to the entity
it's connected to, to simplify this. ... presumably this is done
elsewhere too?

Although then that's still walking 'backwards' rather than forwards...




> }
> }
>
> @@ -43,25 +73,25 @@ static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
> * vimc_streamer_stream_start - Starts streaming for all entities
> * in a stream
> *
> - * @ved: the pointer to the vimc entity initializing the stream
> + * @cved: the pointer to the vimc entity initializing the stream
> *
> * Walks through the entity graph to call vimc_streamer_s_stream()
> * to enable stream in all entities in path of a stream.
> *
> * Return: 0 if success, error code otherwise.
> */
> -static int vimc_streamer_stream_start(struct vimc_stream *stream,
> - struct vimc_ent_device *ved)
> +static int vimc_streamer_stream_start(struct vimc_ent_device *cved)
> {
> struct media_entity *entity;
> struct video_device *vdev;
> struct v4l2_subdev *sd;
> + struct vimc_ent_device *ved = cved;
> int stream_size = 0;
> int ret = 0;
>
> while (stream_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
> if (!ved) {
> - vimc_streamer_pipeline_terminate(stream);
> + vimc_streamer_stream_terminate(cved);
> return -EINVAL;
> }
>
> @@ -71,7 +101,7 @@ static int vimc_streamer_stream_start(struct vimc_stream *stream,
> if (ret && ret != -ENOIOCTLCMD) {
> dev_err(ved->dev, "subdev_call error %s\n",
> ved->ent->name);
> - vimc_streamer_pipeline_terminate(stream);
> + vimc_streamer_stream_terminate(cved);
> return ret;
> }
> }
> @@ -84,7 +114,7 @@ static int vimc_streamer_stream_start(struct vimc_stream *stream,
> dev_err(ved->dev,
> "first entity in the pipe '%s' is not a source\n",
> ved->ent->name);
> - vimc_streamer_pipeline_terminate(stream);
> + vimc_streamer_stream_terminate(cved);
> pr_info ("first entry not source");
> return -EPIPE;
> }
> @@ -104,7 +134,7 @@ static int vimc_streamer_stream_start(struct vimc_stream *stream,
> stream_size++;
> }
>
> - vimc_streamer_pipeline_terminate(stream);
> + vimc_streamer_stream_terminate(cved);
> return -EINVAL;
> }
>
> @@ -120,13 +150,14 @@ static int vimc_streamer_stream_start(struct vimc_stream *stream,
> * Return: 0 if success, error code otherwise.
> */
> static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
> - struct vimc_ent_device *ved)
> + struct vimc_ent_device *cved)
> {
> struct media_entity *entity;
> struct media_device *mdev;
> struct media_graph graph;
> struct video_device *vdev;
> struct v4l2_subdev *sd;
> + struct vimc_ent_device *ved = cved;
> int ret;
>
> entity = ved->ent;
> @@ -170,6 +201,7 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
> }
> }
>
> + vimc_streamer_stream_terminate(cved);
> vimc_streamer_pipeline_terminate(stream);
> return -EINVAL;
> }
> @@ -246,7 +278,7 @@ int vimc_streamer_s_stream(struct vimc_stream *stream,
> if (stream->kthread)
> return 0;
>
> - ret = vimc_streamer_stream_start(stream, ved);
> + ret = vimc_streamer_stream_start(ved);
> if (ret)
> return ret;
>
> @@ -260,6 +292,7 @@ int vimc_streamer_s_stream(struct vimc_stream *stream,
> if (IS_ERR(stream->kthread)) {
> ret = PTR_ERR(stream->kthread);
> dev_err(ved->dev, "kthread_run failed with %d\n", ret);
> + vimc_streamer_stream_terminate(ved);
> vimc_streamer_pipeline_terminate(stream);
> stream->kthread = NULL;

If vimc_streamer_pipeline_terminate() sets stream->kthread = NULL, it
doesn't need to be done again here.


> return ret;
> @@ -269,18 +302,7 @@ int vimc_streamer_s_stream(struct vimc_stream *stream,
> if (!stream->kthread)
> return 0;
>
> - ret = kthread_stop(stream->kthread);
> - /*
> - * kthread_stop returns -EINTR in cases when streamon was
> - * immediately followed by streamoff, and the thread didn't had
> - * a chance to run. Ignore errors to stop the stream in the
> - * pipeline.
> - */

Do we need to retain that comment when stopping the thread?

> - if (ret)
> - dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret);
> -
> - stream->kthread = NULL;
> -
> + vimc_streamer_stream_terminate(ved);
> vimc_streamer_pipeline_terminate(stream);
> }
>
>

--
Regards
--
Kieran