Patchwork [12/18] usb-linux: split large xfers

login
register
mail settings
Submitter Gerd Hoffmann
Date May 16, 2011, 7:56 p.m.
Message ID <1305575782-31766-13-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/95825/
State New
Headers show

Comments

Gerd Hoffmann - May 16, 2011, 7:56 p.m.
Add support for splitting large transfers into multiple smaller ones.
This is needed for the upcoming EHCI emulation which allows guests
to submit requests up to 20k in size.  The linux kernel allows 16k
max size though.

Roughly based on a patch from git://git.kiszka.org/qemu.git ehci

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-linux.c |   68 +++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 43 insertions(+), 25 deletions(-)
David S. Ahern - May 17, 2011, 2:45 a.m.
On 05/16/11 13:56, Gerd Hoffmann wrote:
> Add support for splitting large transfers into multiple smaller ones.
> This is needed for the upcoming EHCI emulation which allows guests
> to submit requests up to 20k in size.  The linux kernel allows 16k
> max size though.
> 
> Roughly based on a patch from git://git.kiszka.org/qemu.git ehci

More specifically:
http://www.mail-archive.com/qemu-devel@nongnu.org/msg30337.html

And "roughly based" is a bit of a stretch. The logic for handling 20k
requests in a 16k limit (the focus of this patch) came from the patch
above. This patch is a forward port based on changes made to the USB
code and some code movement.

David


> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  usb-linux.c |   68 +++++++++++++++++++++++++++++++++++++---------------------
>  1 files changed, 43 insertions(+), 25 deletions(-)
> 
> diff --git a/usb-linux.c b/usb-linux.c
> index b8f7705..b95c119 100644
> --- a/usb-linux.c
> +++ b/usb-linux.c
> @@ -89,6 +89,9 @@ static int usb_fs_type;
>  #define ISO_URB_COUNT 3
>  #define INVALID_EP_TYPE 255
>  
> +/* devio.c limits single requests to 16k */
> +#define MAX_USBFS_BUFFER_SIZE 16384
> +
>  typedef struct AsyncURB AsyncURB;
>  
>  struct endp_data {
> @@ -229,6 +232,7 @@ struct AsyncURB
>  
>      /* For regular async urbs */
>      USBPacket     *packet;
> +    int more; /* large transfer, more urbs follow */
>  
>      /* For buffered iso handling */
>      int iso_frame_idx; /* -1 means in flight */
> @@ -291,7 +295,7 @@ static void async_complete(void *opaque)
>          if (p) {
>              switch (aurb->urb.status) {
>              case 0:
> -                p->len = aurb->urb.actual_length;
> +                p->len += aurb->urb.actual_length;
>                  break;
>  
>              case -EPIPE:
> @@ -306,7 +310,7 @@ static void async_complete(void *opaque)
>  
>              if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
>                  usb_generic_async_ctrl_complete(&s->dev, p);
> -            } else {
> +            } else if (!aurb->more) {
>                  usb_packet_complete(&s->dev, p);
>              }
>          }
> @@ -646,7 +650,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
>      USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
>      struct usbdevfs_urb *urb;
>      AsyncURB *aurb;
> -    int ret;
> +    int ret, rem;
> +    uint8_t *pbuf;
>      uint8_t ep;
>  
>      if (!is_valid(s, p->devep)) {
> @@ -673,32 +678,45 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
>          return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
>      }
>  
> -    aurb = async_alloc(s);
> -    aurb->packet = p;
> +    rem = p->len;
> +    pbuf = p->data;
> +    p->len = 0;
> +    while (rem) {
> +        aurb = async_alloc(s);
> +        aurb->packet = p;
>  
> -    urb = &aurb->urb;
> +        urb = &aurb->urb;
> +        urb->endpoint      = ep;
> +        urb->type          = USBDEVFS_URB_TYPE_BULK;
> +        urb->usercontext   = s;
> +        urb->buffer        = pbuf;
>  
> -    urb->endpoint      = ep;
> -    urb->buffer        = p->data;
> -    urb->buffer_length = p->len;
> -    urb->type          = USBDEVFS_URB_TYPE_BULK;
> -    urb->usercontext   = s;
> +        if (rem > MAX_USBFS_BUFFER_SIZE) {
> +            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
> +            aurb->more         = 1;
> +        } else {
> +            urb->buffer_length = rem;
> +            aurb->more         = 0;
> +        }
> +        pbuf += urb->buffer_length;
> +        rem  -= urb->buffer_length;
>  
> -    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
> +        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
>  
> -    DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
> -            urb->endpoint, p->len, aurb);
> +        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
> +                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
>  
> -    if (ret < 0) {
> -        DPRINTF("husb: submit failed. errno %d\n", errno);
> -        async_free(aurb);
> +        if (ret < 0) {
> +            DPRINTF("husb: submit failed. errno %d\n", errno);
> +            async_free(aurb);
>  
> -        switch(errno) {
> -        case ETIMEDOUT:
> -            return USB_RET_NAK;
> -        case EPIPE:
> -        default:
> -            return USB_RET_STALL;
> +            switch(errno) {
> +            case ETIMEDOUT:
> +                return USB_RET_NAK;
> +            case EPIPE:
> +            default:
> +                return USB_RET_STALL;
> +            }
>          }
>      }
>
Gerd Hoffmann - May 17, 2011, 7:10 a.m.
Hi,

> More specifically:
> http://www.mail-archive.com/qemu-devel@nongnu.org/msg30337.html

Reference added to the commit message.

cheers,
   Gerd

Patch

diff --git a/usb-linux.c b/usb-linux.c
index b8f7705..b95c119 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -89,6 +89,9 @@  static int usb_fs_type;
 #define ISO_URB_COUNT 3
 #define INVALID_EP_TYPE 255
 
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
 typedef struct AsyncURB AsyncURB;
 
 struct endp_data {
@@ -229,6 +232,7 @@  struct AsyncURB
 
     /* For regular async urbs */
     USBPacket     *packet;
+    int more; /* large transfer, more urbs follow */
 
     /* For buffered iso handling */
     int iso_frame_idx; /* -1 means in flight */
@@ -291,7 +295,7 @@  static void async_complete(void *opaque)
         if (p) {
             switch (aurb->urb.status) {
             case 0:
-                p->len = aurb->urb.actual_length;
+                p->len += aurb->urb.actual_length;
                 break;
 
             case -EPIPE:
@@ -306,7 +310,7 @@  static void async_complete(void *opaque)
 
             if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
                 usb_generic_async_ctrl_complete(&s->dev, p);
-            } else {
+            } else if (!aurb->more) {
                 usb_packet_complete(&s->dev, p);
             }
         }
@@ -646,7 +650,8 @@  static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     struct usbdevfs_urb *urb;
     AsyncURB *aurb;
-    int ret;
+    int ret, rem;
+    uint8_t *pbuf;
     uint8_t ep;
 
     if (!is_valid(s, p->devep)) {
@@ -673,32 +678,45 @@  static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
     }
 
-    aurb = async_alloc(s);
-    aurb->packet = p;
+    rem = p->len;
+    pbuf = p->data;
+    p->len = 0;
+    while (rem) {
+        aurb = async_alloc(s);
+        aurb->packet = p;
 
-    urb = &aurb->urb;
+        urb = &aurb->urb;
+        urb->endpoint      = ep;
+        urb->type          = USBDEVFS_URB_TYPE_BULK;
+        urb->usercontext   = s;
+        urb->buffer        = pbuf;
 
-    urb->endpoint      = ep;
-    urb->buffer        = p->data;
-    urb->buffer_length = p->len;
-    urb->type          = USBDEVFS_URB_TYPE_BULK;
-    urb->usercontext   = s;
+        if (rem > MAX_USBFS_BUFFER_SIZE) {
+            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+            aurb->more         = 1;
+        } else {
+            urb->buffer_length = rem;
+            aurb->more         = 0;
+        }
+        pbuf += urb->buffer_length;
+        rem  -= urb->buffer_length;
 
-    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
 
-    DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
-            urb->endpoint, p->len, aurb);
+        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
 
-    if (ret < 0) {
-        DPRINTF("husb: submit failed. errno %d\n", errno);
-        async_free(aurb);
+        if (ret < 0) {
+            DPRINTF("husb: submit failed. errno %d\n", errno);
+            async_free(aurb);
 
-        switch(errno) {
-        case ETIMEDOUT:
-            return USB_RET_NAK;
-        case EPIPE:
-        default:
-            return USB_RET_STALL;
+            switch(errno) {
+            case ETIMEDOUT:
+                return USB_RET_NAK;
+            case EPIPE:
+            default:
+                return USB_RET_STALL;
+            }
         }
     }