staging: bcm2835-camera: Do not bulk receive from service thread
authorDave Stevenson <dave.stevenson@raspberrypi.org>
Sat, 29 Jun 2019 12:13:18 +0000 (14:13 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Jul 2019 07:09:06 +0000 (09:09 +0200)
vchi_bulk_queue_receive will queue up to a default of 4
bulk receives on a connection before blocking.
If called from the VCHI service_callback thread, then
that thread is unable to service the VCHI_CALLBACK_BULK_RECEIVED
events that would enable the queue call to succeed.

Add a workqueue to schedule the call vchi_bulk_queue_receive
in an alternate context to avoid the lock up.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Acked-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.c

index f1bb900c4aa6cc0e6bb3fa4614c17f0019841632..1a343d8e5e7880b574f1e5287b7d54220b6997f2 100644 (file)
@@ -117,8 +117,10 @@ struct mmal_msg_context {
 
        union {
                struct {
-                       /* work struct for defered callback - must come first */
+                       /* work struct for buffer_cb callback */
                        struct work_struct work;
+                       /* work struct for deferred callback */
+                       struct work_struct buffer_to_host_work;
                        /* mmal instance */
                        struct vchiq_mmal_instance *instance;
                        /* mmal port */
@@ -167,6 +169,9 @@ struct vchiq_mmal_instance {
        /* component to use next */
        int component_idx;
        struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS];
+
+       /* ordered workqueue to process all bulk operations */
+       struct workqueue_struct *bulk_wq;
 };
 
 static struct mmal_msg_context *
@@ -248,7 +253,44 @@ static void buffer_work_cb(struct work_struct *work)
                                            msg_context->u.bulk.mmal_flags,
                                            msg_context->u.bulk.dts,
                                            msg_context->u.bulk.pts);
+}
 
+/* workqueue scheduled callback to handle receiving buffers
+ *
+ * VCHI will allow up to 4 bulk receives to be scheduled before blocking.
+ * If we block in the service_callback context then we can't process the
+ * VCHI_CALLBACK_BULK_RECEIVED message that would otherwise allow the blocked
+ * vchi_bulk_queue_receive() call to complete.
+ */
+static void buffer_to_host_work_cb(struct work_struct *work)
+{
+       struct mmal_msg_context *msg_context =
+               container_of(work, struct mmal_msg_context,
+                            u.bulk.buffer_to_host_work);
+       struct vchiq_mmal_instance *instance = msg_context->instance;
+       unsigned long len = msg_context->u.bulk.buffer_used;
+       int ret;
+
+       if (!len)
+               /* Dummy receive to ensure the buffers remain in order */
+               len = 8;
+       /* queue the bulk submission */
+       vchi_service_use(instance->handle);
+       ret = vchi_bulk_queue_receive(instance->handle,
+                                     msg_context->u.bulk.buffer->buffer,
+                                     /* Actual receive needs to be a multiple
+                                      * of 4 bytes
+                                      */
+                                     (len + 3) & ~3,
+                                     VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE |
+                                     VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
+                                     msg_context);
+
+       vchi_service_release(instance->handle);
+
+       if (ret != 0)
+               pr_err("%s: ctx: %p, vchi_bulk_queue_receive failed %d\n",
+                      __func__, msg_context, ret);
 }
 
 /* enqueue a bulk receive for a given message context */
@@ -257,7 +299,6 @@ static int bulk_receive(struct vchiq_mmal_instance *instance,
                        struct mmal_msg_context *msg_context)
 {
        unsigned long rd_len;
-       int ret;
 
        rd_len = msg->u.buffer_from_host.buffer_header.length;
 
@@ -293,45 +334,10 @@ static int bulk_receive(struct vchiq_mmal_instance *instance,
        msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts;
        msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts;
 
-       /* queue the bulk submission */
-       vchi_service_use(instance->handle);
-       ret = vchi_bulk_queue_receive(instance->handle,
-                                     msg_context->u.bulk.buffer->buffer,
-                                     /* Actual receive needs to be a multiple
-                                      * of 4 bytes
-                                      */
-                                     (rd_len + 3) & ~3,
-                                     VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE |
-                                     VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
-                                     msg_context);
-
-       vchi_service_release(instance->handle);
+       queue_work(msg_context->instance->bulk_wq,
+                  &msg_context->u.bulk.buffer_to_host_work);
 
-       return ret;
-}
-
-/* enque a dummy bulk receive for a given message context */
-static int dummy_bulk_receive(struct vchiq_mmal_instance *instance,
-                             struct mmal_msg_context *msg_context)
-{
-       int ret;
-
-       /* zero length indicates this was a dummy transfer */
-       msg_context->u.bulk.buffer_used = 0;
-
-       /* queue the bulk submission */
-       vchi_service_use(instance->handle);
-
-       ret = vchi_bulk_queue_receive(instance->handle,
-                                     instance->bulk_scratch,
-                                     8,
-                                     VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE |
-                                     VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
-                                     msg_context);
-
-       vchi_service_release(instance->handle);
-
-       return ret;
+       return 0;
 }
 
 /* data in message, memcpy from packet into output buffer */
@@ -379,6 +385,8 @@ buffer_from_host(struct vchiq_mmal_instance *instance,
 
        /* initialise work structure ready to schedule callback */
        INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb);
+       INIT_WORK(&msg_context->u.bulk.buffer_to_host_work,
+                 buffer_to_host_work_cb);
 
        /* prep the buffer from host message */
        memset(&m, 0xbc, sizeof(m));    /* just to make debug clearer */
@@ -459,7 +467,7 @@ static void buffer_to_host_cb(struct vchiq_mmal_instance *instance,
                if (msg->u.buffer_from_host.buffer_header.flags &
                    MMAL_BUFFER_HEADER_FLAG_EOS) {
                        msg_context->u.bulk.status =
-                           dummy_bulk_receive(instance, msg_context);
+                           bulk_receive(instance, msg, msg_context);
                        if (msg_context->u.bulk.status == 0)
                                return; /* successful bulk submission, bulk
                                         * completion will trigger callback
@@ -1793,6 +1801,9 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance)
 
        mutex_unlock(&instance->vchiq_mutex);
 
+       flush_workqueue(instance->bulk_wq);
+       destroy_workqueue(instance->bulk_wq);
+
        vfree(instance->bulk_scratch);
 
        idr_destroy(&instance->context_map);
@@ -1855,6 +1866,11 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance)
 
        params.callback_param = instance;
 
+       instance->bulk_wq = alloc_ordered_workqueue("mmal-vchiq",
+                                                   WQ_MEM_RECLAIM);
+       if (!instance->bulk_wq)
+               goto err_free;
+
        status = vchi_service_open(vchi_instance, &params, &instance->handle);
        if (status) {
                pr_err("Failed to open VCHI service connection (status=%d)\n",
@@ -1869,8 +1885,9 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance)
        return 0;
 
 err_close_services:
-
        vchi_service_close(instance->handle);
+       destroy_workqueue(instance->bulk_wq);
+err_free:
        vfree(instance->bulk_scratch);
        kfree(instance);
        return -ENODEV;