Hi Guennadi, Thank you for taking the time to review this series, On 04/01/18 18:54, Guennadi Liakhovetski wrote: > On Wed, 3 Jan 2018, Kieran Bingham wrote: > >> From: Kieran Bingham <kieran.bingham@xxxxxxxxxxxxxxxx> >> >> Newer high definition cameras, and cameras with multiple lenses such as >> the range of stereovision cameras now available have ever increasing >> data rates. >> >> The inclusion of a variable length packet header in URB packets mean >> that we must memcpy the frame data out to our destination 'manually'. >> This can result in data rates of up to 2 gigabits per second being >> processed. >> >> To improve efficiency, and maximise throughput, handle the URB decode >> processing through a work queue to move it from interrupt context, and >> allow multiple processors to work on URBs in parallel. >> >> Signed-off-by: Kieran Bingham <kieran.bingham@xxxxxxxxxxxxxxxx> >> --- >> drivers/media/usb/uvc/uvc_queue.c | 12 +++- >> drivers/media/usb/uvc/uvc_video.c | 114 ++++++++++++++++++++++++++----- >> drivers/media/usb/uvc/uvcvideo.h | 24 +++++++- >> 3 files changed, 132 insertions(+), 18 deletions(-) >> >> diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c >> index 204dd91a8526..07fcbfc132c9 100644 >> --- a/drivers/media/usb/uvc/uvc_queue.c >> +++ b/drivers/media/usb/uvc/uvc_queue.c >> @@ -179,10 +179,22 @@ static void uvc_stop_streaming(struct vb2_queue *vq) >> struct uvc_video_queue *queue = vb2_get_drv_priv(vq); >> struct uvc_streaming *stream = uvc_queue_to_stream(queue); >> >> + /* Prevent new buffers coming in. */ >> + spin_lock_irq(&queue->irqlock); >> + queue->flags |= UVC_QUEUE_STOPPING; >> + spin_unlock_irq(&queue->irqlock); >> + >> + /* >> + * All pending work should be completed before disabling the stream, as >> + * all URBs will be free'd during uvc_video_enable(s, 0). >> + */ >> + flush_workqueue(stream->async_wq); >> + >> uvc_video_enable(stream, 0); >> >> spin_lock_irq(&queue->irqlock); >> uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR); >> + queue->flags &= ~UVC_QUEUE_STOPPING; >> spin_unlock_irq(&queue->irqlock); >> } >> >> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c >> index 045ac655313c..b7b32a6bc2dc 100644 >> --- a/drivers/media/usb/uvc/uvc_video.c >> +++ b/drivers/media/usb/uvc/uvc_video.c >> @@ -1058,21 +1058,70 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, >> return data[0]; >> } >> >> -static void uvc_video_decode_data(struct uvc_streaming *stream, >> - struct uvc_buffer *buf, const __u8 *data, int len) >> +/* >> + * uvc_video_decode_data_work: Asynchronous memcpy processing >> + * >> + * Perform memcpy tasks in process context, with completion handlers >> + * to return the URB, and buffer handles. >> + * >> + * The work submitter must pre-determine that the work is safe >> + */ >> +static void uvc_video_decode_data_work(struct work_struct *work) >> { >> - unsigned int maxlen, nbytes; >> - void *mem; >> + struct uvc_urb *uvc_urb = container_of(work, struct uvc_urb, work); >> + struct uvc_streaming *stream = uvc_urb->stream; >> + struct uvc_video_queue *queue = &stream->queue; >> + unsigned int i; >> + bool stopping; >> + int ret; >> + >> + for (i = 0; i < uvc_urb->packets; i++) { >> + struct uvc_decode_op *op = &uvc_urb->decodes[i]; >> + >> + memcpy(op->dst, op->src, op->len); >> + >> + /* Release reference taken on this buffer */ >> + uvc_queue_buffer_release(op->buf); >> + } >> + >> + /* >> + * Prevent resubmitting URBs when shutting down to ensure that no new >> + * work item will be scheduled after uvc_stop_streaming() flushes the >> + * work queue. >> + */ >> + spin_lock_irq(&queue->irqlock); >> + stopping = queue->flags & UVC_QUEUE_STOPPING; >> + spin_unlock_irq(&queue->irqlock); > > Are you sure this locking really helps? What if uvc_stop_streaming() runs > here? Quite - there is a race there. It protects the flag though ;-) I thought I had pulled the lock out to only check the flag, to prevent surrounding the usb_submit_urb(), but now I look again - I see no reason not to lock for the whole critical section. I've tested locally on my laptop, and it seems safe to hold the lock during the usb_submit_urb() (unless anyone sees something I'm missing), so I'll update this for the next spin to something more like: spin_lock_irq(&queue->irqlock); if (!(queue->flags & UVC_QUEUE_STOPPING)) { ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC); if (ret < 0) uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", ret); } spin_unlock_irq(&queue->irqlock); > Thanks > Guennadi > >> + >> + if (stopping) >> + return; >> + >> + ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC); >> + if (ret < 0) >> + uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", >> + ret); >> +} Regards -- Kieran