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>
/*
* 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
/* 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;
}
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]);
+ }
}
}
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)
{
{
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;
}
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);
}
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);
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++;
}
{
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]);
/*
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);
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;
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))
* @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.
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;
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,