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.

Signed-off-by: Ezequiel Garcia <ezequ...@collabora.com>
---
 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;
 };
-- 
2.18.0.rc2

Reply via email to