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>
Thu, 3 Nov 2022 14:59:11 +0000 (23:59 +0900)
commit 0a0a2760b04814428800d48281a447a7522470ad upstream.

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 ec299f5cc65a52c77792f65ee90de0d3763c042a..0cc8422afe4e2add7120c109e6b634b6660ad7c9 100644 (file)
@@ -313,6 +313,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)
@@ -338,10 +339,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 1889d75f87881a818d7205b84f5f17f37e81cef1..15e2202cf20392657d9c67f3980748ae08585543 100644 (file)
@@ -59,6 +59,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;
 
@@ -84,13 +85,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;
 }
@@ -151,7 +153,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;
@@ -166,6 +169,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;
 
@@ -180,12 +184,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;
        }
 }
 
@@ -222,6 +227,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);