On 12/07/18 17:43, Ezequiel Garcia wrote:
> v4l2_m2m_job_finish() is typically called in interrupt context.
>
> Some implementation of .device_run might sleep, and so it's
> desirable to avoid calling it directly from
> v4l2_m2m_job_finish(), thus avoiding .device_run from running
> in interrupt context.
>
> Implement a deferred context that gets scheduled by
> v4l2_m2m_job_finish().
>
> The worker calls v4l2_m2m_try_schedule(), which makes sure
> a single job is running at the same time, so it's safe to
> call it from different executions context.
I am not sure about this. I think that the only thing that needs to
run in the work queue is the call to device_run. Everything else
up to that point runs fine in interrupt context.
While we're on it: I see that v4l2_m2m_prepare_buf() also calls
v4l2_m2m_try_schedule(): I don't think it should do that since
prepare_buf does not actually queue a new buffer to the driver.
Regards,
Hans
>
> Signed-off-by: Ezequiel Garcia <[email protected]>
> ---
> drivers/media/v4l2-core/v4l2-mem2mem.c | 14 +++++++++++++-
> include/media/v4l2-mem2mem.h | 2 ++
> 2 files changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c
> b/drivers/media/v4l2-core/v4l2-mem2mem.c
> index c2e9c2b7dcd1..1d0e20809ffe 100644
> --- a/drivers/media/v4l2-core/v4l2-mem2mem.c
> +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
> @@ -258,6 +258,17 @@ void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
> }
> EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule);
>
> +/**
> + * v4l2_m2m_try_schedule_work() - run pending jobs for the context
> + * @work: Work structure used for scheduling the execution of this function.
> + */
> +static void v4l2_m2m_try_schedule_work(struct work_struct *work)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx =
> + container_of(work, struct v4l2_m2m_ctx, job_work);
> + v4l2_m2m_try_schedule(m2m_ctx);
> +}
> +
> /**
> * v4l2_m2m_cancel_job() - cancel pending jobs for the context
> * @m2m_ctx: m2m context with jobs to be canceled
> @@ -316,7 +327,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
> /* This instance might have more buffers ready, but since we do not
> * allow more than one job on the job_queue per instance, each has
> * to be scheduled separately after the previous one finishes. */
> - v4l2_m2m_try_schedule(m2m_ctx);
> + schedule_work(&m2m_ctx->job_work);
> }
> EXPORT_SYMBOL(v4l2_m2m_job_finish);
>
> @@ -614,6 +625,7 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct
> v4l2_m2m_dev *m2m_dev,
> m2m_ctx->priv = drv_priv;
> m2m_ctx->m2m_dev = m2m_dev;
> init_waitqueue_head(&m2m_ctx->finished);
> + INIT_WORK(&m2m_ctx->job_work, v4l2_m2m_try_schedule_work);
>
> out_q_ctx = &m2m_ctx->out_q_ctx;
> cap_q_ctx = &m2m_ctx->cap_q_ctx;
> diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
> index 3d07ba3a8262..2543fbfdd2b1 100644
> --- a/include/media/v4l2-mem2mem.h
> +++ b/include/media/v4l2-mem2mem.h
> @@ -89,6 +89,7 @@ struct v4l2_m2m_queue_ctx {
> * @job_flags: Job queue flags, used internally by v4l2-mem2mem.c:
> * %TRANS_QUEUED, %TRANS_RUNNING and %TRANS_ABORT.
> * @finished: Wait queue used to signalize when a job queue finished.
> + * @job_work: Worker to run queued jobs.
> * @priv: Instance private data
> *
> * The memory to memory context is specific to a file handle, NOT to e.g.
> @@ -109,6 +110,7 @@ struct v4l2_m2m_ctx {
> struct list_head queue;
> unsigned long job_flags;
> wait_queue_head_t finished;
> + struct work_struct job_work;
>
> void *priv;
> };
>