usb: gadget: uvc: fix sg handling in error case
authorDan Vacura <w36195@motorola.com>
Tue, 18 Oct 2022 21:50:39 +0000 (16:50 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 22 Oct 2022 10:30:35 +0000 (12:30 +0200)
If there is a transmission error the buffer will be returned too early,
causing a memory fault as subsequent requests for that buffer are still
queued up to be sent. Refactor the error handling to wait for the final
request to come in before reporting back the buffer to userspace for all
transfer types (bulk/isoc/isoc_sg). This ensures userspace knows if the
frame was successfully sent.

Fixes: e81e7f9a0eb9 ("usb: gadget: uvc: add scatter gather support")
Cc: <stable@vger.kernel.org>
Signed-off-by: Dan Vacura <w36195@motorola.com>
Link: https://lore.kernel.org/r/20221018215044.765044-4-w36195@motorola.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/uvc_queue.c
drivers/usb/gadget/function/uvc_video.c

index ec500ee499eed1e099dd9d4d673320f30e47e0c8..0aa3d7e1f3cc32c696e422196cf1d1e9ab9a34de 100644 (file)
@@ -304,6 +304,7 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
 
                queue->sequence = 0;
                queue->buf_used = 0;
+               queue->flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
        } else {
                ret = vb2_streamoff(&queue->queue, queue->queue.type);
                if (ret < 0)
@@ -329,10 +330,11 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
 void uvcg_complete_buffer(struct uvc_video_queue *queue,
                                          struct uvc_buffer *buf)
 {
-       if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
-            buf->length != buf->bytesused) {
-               buf->state = UVC_BUF_STATE_QUEUED;
+       if (queue->flags & UVC_QUEUE_DROP_INCOMPLETE) {
+               queue->flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
+               buf->state = UVC_BUF_STATE_ERROR;
                vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
+               vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR);
                return;
        }
 
index 323977716f5a49c95fcd1e91981946dd1307c97b..5993e083819cb842e550fba9c71856a0fac90db0 100644 (file)
@@ -88,6 +88,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
                struct uvc_buffer *buf)
 {
        void *mem = req->buf;
+       struct uvc_request *ureq = req->context;
        int len = video->req_size;
        int ret;
 
@@ -113,13 +114,14 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
                list_del(&buf->queue);
-               uvcg_complete_buffer(&video->queue, buf);
                video->fid ^= UVC_STREAM_FID;
+               ureq->last_buf = buf;
 
                video->payload_size = 0;
        }
 
        if (video->payload_size == video->max_payload_size ||
+           video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE ||
            buf->bytesused == video->queue.buf_used)
                video->payload_size = 0;
 }
@@ -180,7 +182,8 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
        req->length -= len;
        video->queue.buf_used += req->length - header_len;
 
-       if (buf->bytesused == video->queue.buf_used || !buf->sg) {
+       if (buf->bytesused == video->queue.buf_used || !buf->sg ||
+                       video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
                buf->offset = 0;
@@ -195,6 +198,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
                struct uvc_buffer *buf)
 {
        void *mem = req->buf;
+       struct uvc_request *ureq = req->context;
        int len = video->req_size;
        int ret;
 
@@ -209,12 +213,13 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
 
        req->length = video->req_size - len;
 
-       if (buf->bytesused == video->queue.buf_used) {
+       if (buf->bytesused == video->queue.buf_used ||
+                       video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
                list_del(&buf->queue);
-               uvcg_complete_buffer(&video->queue, buf);
                video->fid ^= UVC_STREAM_FID;
+               ureq->last_buf = buf;
        }
 }
 
@@ -255,6 +260,11 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
        case 0:
                break;
 
+       case -EXDEV:
+               uvcg_dbg(&video->uvc->func, "VS request missed xfer.\n");
+               queue->flags |= UVC_QUEUE_DROP_INCOMPLETE;
+               break;
+
        case -ESHUTDOWN:        /* disconnect from host. */
                uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
                uvcg_queue_cancel(queue, 1);