USB: serial: add support for multiple read urbs
authorJohan Hovold <jhovold@gmail.com>
Sun, 6 Nov 2011 18:06:37 +0000 (19:06 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Nov 2011 18:46:13 +0000 (10:46 -0800)
Add support for multiple read urbs to generic read implementation.

Use a static array of two read urbs for now which is enough to get a
50% throughput increase in one test setup.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/generic.c
drivers/usb/serial/usb-serial.c
include/linux/usb/serial.h

index e4db5ad2bc55f1c37f2da2b15c086534e98248c7..f7403576f99f6d04dfc28b0018875ffc1019a019 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * USB Serial Converter Generic functions
  *
- * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com)
+ * Copyright (C) 2010 - 2011 Johan Hovold (jhovold@gmail.com)
  * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
  *
  *     This program is free software; you can redistribute it and/or
@@ -132,7 +132,7 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port
 
        /* if we have a bulk endpoint, start reading from it */
        if (port->bulk_in_size)
-               result = usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+               result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
 
        return result;
 }
@@ -157,8 +157,10 @@ static void generic_cleanup(struct usb_serial_port *port)
                        kfifo_reset_out(&port->write_fifo);
                        spin_unlock_irqrestore(&port->lock, flags);
                }
-               if (port->bulk_in_size)
-                       usb_kill_urb(port->read_urb);
+               if (port->bulk_in_size) {
+                       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+                               usb_kill_urb(port->read_urbs[i]);
+               }
        }
 }
 
@@ -308,19 +310,52 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
        return chars;
 }
 
-int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+                                               int index, gfp_t mem_flags)
+{
+       int res;
+
+       if (!test_and_clear_bit(index, &port->read_urbs_free))
+               return 0;
+
+       dbg("%s - port %d, urb %d\n", __func__, port->number, index);
+
+       res = usb_submit_urb(port->read_urbs[index], mem_flags);
+       if (res) {
+               if (res != -EPERM) {
+                       dev_err(&port->dev,
+                                       "%s - usb_submit_urb failed: %d\n",
+                                       __func__, res);
+               }
+               set_bit(index, &port->read_urbs_free);
+               return res;
+       }
+
+       return 0;
+}
+
+int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
                                        gfp_t mem_flags)
 {
-       int result;
+       int res;
+       int i;
 
-       result = usb_submit_urb(port->read_urb, mem_flags);
-       if (result && result != -EPERM) {
-               dev_err(&port->dev, "%s - error submitting urb: %d\n",
-                                                       __func__, result);
+       dbg("%s - port %d", __func__, port->number);
+
+       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+               res = usb_serial_generic_submit_read_urb(port, i, mem_flags);
+               if (res)
+                       goto err;
        }
-       return result;
+
+       return 0;
+err:
+       for (; i >= 0; --i)
+               usb_kill_urb(port->read_urbs[i]);
+
+       return res;
 }
-EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urb);
+EXPORT_SYMBOL_GPL(usb_serial_generic_submit_read_urbs);
 
 void usb_serial_generic_process_read_urb(struct urb *urb)
 {
@@ -356,14 +391,19 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
        unsigned char *data = urb->transfer_buffer;
-       int status = urb->status;
        unsigned long flags;
+       int i;
 
-       dbg("%s - port %d", __func__, port->number);
+       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+               if (urb == port->read_urbs[i])
+                       break;
+       }
+       set_bit(i, &port->read_urbs_free);
 
-       if (unlikely(status != 0)) {
-               dbg("%s - nonzero read bulk status received: %d",
-                   __func__, status);
+       dbg("%s - port %d, urb %d, len %d\n", __func__, port->number, i,
+                                                       urb->actual_length);
+       if (urb->status) {
+               dbg("%s - non-zero urb status: %d\n", __func__, urb->status);
                return;
        }
 
@@ -376,7 +416,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
        port->throttled = port->throttle_req;
        if (!port->throttled) {
                spin_unlock_irqrestore(&port->lock, flags);
-               usb_serial_generic_submit_read_urb(port, GFP_ATOMIC);
+               usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
        } else
                spin_unlock_irqrestore(&port->lock, flags);
 }
@@ -443,7 +483,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
        spin_unlock_irq(&port->lock);
 
        if (was_throttled)
-               usb_serial_generic_submit_read_urb(port, GFP_KERNEL);
+               usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle);
 
@@ -509,8 +549,9 @@ int usb_serial_generic_resume(struct usb_serial *serial)
                if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
                        continue;
 
-               if (port->read_urb) {
-                       r = usb_submit_urb(port->read_urb, GFP_NOIO);
+               if (port->bulk_in_size) {
+                       r = usb_serial_generic_submit_read_urbs(port,
+                                                               GFP_NOIO);
                        if (r < 0)
                                c++;
                }
index bfbe6e5431d351454624f1a0f98aa0d12b6936f8..8d5190f3d8ebaa64b1b83ebdc34a73924a1202d4 100644 (file)
@@ -562,7 +562,8 @@ static void kill_traffic(struct usb_serial_port *port)
 {
        int i;
 
-       usb_kill_urb(port->read_urb);
+       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i)
+               usb_kill_urb(port->read_urbs[i]);
        for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
                usb_kill_urb(port->write_urbs[i]);
        /*
@@ -594,15 +595,17 @@ static void port_release(struct device *dev)
        kill_traffic(port);
        cancel_work_sync(&port->work);
 
-       usb_free_urb(port->read_urb);
        usb_free_urb(port->interrupt_in_urb);
        usb_free_urb(port->interrupt_out_urb);
+       for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
+               usb_free_urb(port->read_urbs[i]);
+               kfree(port->bulk_in_buffers[i]);
+       }
        for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
                usb_free_urb(port->write_urbs[i]);
                kfree(port->bulk_out_buffers[i]);
        }
        kfifo_free(&port->write_fifo);
-       kfree(port->bulk_in_buffer);
        kfree(port->interrupt_in_buffer);
        kfree(port->interrupt_out_buffer);
        kfree(port);
@@ -721,6 +724,7 @@ int usb_serial_probe(struct usb_interface *interface,
        unsigned int minor;
        int buffer_size;
        int i;
+       int j;
        int num_interrupt_in = 0;
        int num_interrupt_out = 0;
        int num_bulk_in = 0;
@@ -903,31 +907,39 @@ int usb_serial_probe(struct usb_interface *interface,
        for (i = 0; i < num_bulk_in; ++i) {
                endpoint = bulk_in_endpoint[i];
                port = serial->port[i];
-               port->read_urb = usb_alloc_urb(0, GFP_KERNEL);
-               if (!port->read_urb) {
-                       dev_err(&interface->dev, "No free urbs available\n");
-                       goto probe_error;
-               }
                buffer_size = max_t(int, serial->type->bulk_in_size,
                                usb_endpoint_maxp(endpoint));
                port->bulk_in_size = buffer_size;
                port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
-               port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
-               if (!port->bulk_in_buffer) {
-                       dev_err(&interface->dev,
+
+               for (j = 0; j < ARRAY_SIZE(port->read_urbs); ++j) {
+                       set_bit(j, &port->read_urbs_free);
+                       port->read_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
+                       if (!port->read_urbs[j]) {
+                               dev_err(&interface->dev,
+                                               "No free urbs available\n");
+                               goto probe_error;
+                       }
+                       port->bulk_in_buffers[j] = kmalloc(buffer_size,
+                                                               GFP_KERNEL);
+                       if (!port->bulk_in_buffers[j]) {
+                               dev_err(&interface->dev,
                                        "Couldn't allocate bulk_in_buffer\n");
-                       goto probe_error;
-               }
-               usb_fill_bulk_urb(port->read_urb, dev,
-                               usb_rcvbulkpipe(dev,
+                               goto probe_error;
+                       }
+                       usb_fill_bulk_urb(port->read_urbs[j], dev,
+                                       usb_rcvbulkpipe(dev,
                                                endpoint->bEndpointAddress),
-                               port->bulk_in_buffer, buffer_size,
-                               serial->type->read_bulk_callback, port);
+                                       port->bulk_in_buffers[j], buffer_size,
+                                       serial->type->read_bulk_callback,
+                                       port);
+               }
+
+               port->read_urb = port->read_urbs[0];
+               port->bulk_in_buffer = port->bulk_in_buffers[0];
        }
 
        for (i = 0; i < num_bulk_out; ++i) {
-               int j;
-
                endpoint = bulk_out_endpoint[i];
                port = serial->port[i];
                if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
index 8ccd405e9005ea8d0860dd0e90afd179099ea6cf..4267a9c717baac379045ad953746e2b0569b9b95 100644 (file)
@@ -58,6 +58,9 @@ enum port_dev_state {
  * @read_urb: pointer to the bulk in struct urb for this port.
  * @bulk_in_endpointAddress: endpoint address for the bulk in pipe for this
  *     port.
+ * @bulk_in_buffers: pointers to the bulk in buffers for this port
+ * @read_urbs: pointers to the bulk in urbs for this port
+ * @read_urbs_free: status bitmap the for bulk in urbs
  * @bulk_out_buffer: pointer to the bulk out buffer for this port.
  * @bulk_out_size: the size of the bulk_out_buffer, in bytes.
  * @write_urb: pointer to the bulk out struct urb for this port.
@@ -98,6 +101,10 @@ struct usb_serial_port {
        struct urb              *read_urb;
        __u8                    bulk_in_endpointAddress;
 
+       unsigned char           *bulk_in_buffers[2];
+       struct urb              *read_urbs[2];
+       unsigned long           read_urbs_free;
+
        unsigned char           *bulk_out_buffer;
        int                     bulk_out_size;
        struct urb              *write_urb;
@@ -338,7 +345,7 @@ extern void usb_serial_generic_disconnect(struct usb_serial *serial);
 extern void usb_serial_generic_release(struct usb_serial *serial);
 extern int usb_serial_generic_register(int debug);
 extern void usb_serial_generic_deregister(void);
-extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
+extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
                                                 gfp_t mem_flags);
 extern void usb_serial_generic_process_read_urb(struct urb *urb);
 extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,