USB: serial: add support for multiple read urbs
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>
This commit is contained in:
parent
f5230a53c1
commit
d83b405383
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* USB Serial Converter Generic functions
|
* 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)
|
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* 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 we have a bulk endpoint, start reading from it */
|
||||||
if (port->bulk_in_size)
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -157,8 +157,10 @@ static void generic_cleanup(struct usb_serial_port *port)
|
||||||
kfifo_reset_out(&port->write_fifo);
|
kfifo_reset_out(&port->write_fifo);
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
}
|
}
|
||||||
if (port->bulk_in_size)
|
if (port->bulk_in_size) {
|
||||||
usb_kill_urb(port->read_urb);
|
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;
|
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)
|
gfp_t mem_flags)
|
||||||
{
|
{
|
||||||
int result;
|
int res;
|
||||||
|
int i;
|
||||||
|
|
||||||
result = usb_submit_urb(port->read_urb, mem_flags);
|
dbg("%s - port %d", __func__, port->number);
|
||||||
if (result && result != -EPERM) {
|
|
||||||
dev_err(&port->dev, "%s - error submitting urb: %d\n",
|
for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
|
||||||
__func__, result);
|
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)
|
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;
|
struct usb_serial_port *port = urb->context;
|
||||||
unsigned char *data = urb->transfer_buffer;
|
unsigned char *data = urb->transfer_buffer;
|
||||||
int status = urb->status;
|
|
||||||
unsigned long flags;
|
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 - port %d, urb %d, len %d\n", __func__, port->number, i,
|
||||||
dbg("%s - nonzero read bulk status received: %d",
|
urb->actual_length);
|
||||||
__func__, status);
|
if (urb->status) {
|
||||||
|
dbg("%s - non-zero urb status: %d\n", __func__, urb->status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +416,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
|
||||||
port->throttled = port->throttle_req;
|
port->throttled = port->throttle_req;
|
||||||
if (!port->throttled) {
|
if (!port->throttled) {
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
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
|
} else
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -443,7 +483,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
|
||||||
spin_unlock_irq(&port->lock);
|
spin_unlock_irq(&port->lock);
|
||||||
|
|
||||||
if (was_throttled)
|
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);
|
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))
|
if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (port->read_urb) {
|
if (port->bulk_in_size) {
|
||||||
r = usb_submit_urb(port->read_urb, GFP_NOIO);
|
r = usb_serial_generic_submit_read_urbs(port,
|
||||||
|
GFP_NOIO);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -562,7 +562,8 @@ static void kill_traffic(struct usb_serial_port *port)
|
||||||
{
|
{
|
||||||
int i;
|
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)
|
for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
|
||||||
usb_kill_urb(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);
|
kill_traffic(port);
|
||||||
cancel_work_sync(&port->work);
|
cancel_work_sync(&port->work);
|
||||||
|
|
||||||
usb_free_urb(port->read_urb);
|
|
||||||
usb_free_urb(port->interrupt_in_urb);
|
usb_free_urb(port->interrupt_in_urb);
|
||||||
usb_free_urb(port->interrupt_out_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) {
|
for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
|
||||||
usb_free_urb(port->write_urbs[i]);
|
usb_free_urb(port->write_urbs[i]);
|
||||||
kfree(port->bulk_out_buffers[i]);
|
kfree(port->bulk_out_buffers[i]);
|
||||||
}
|
}
|
||||||
kfifo_free(&port->write_fifo);
|
kfifo_free(&port->write_fifo);
|
||||||
kfree(port->bulk_in_buffer);
|
|
||||||
kfree(port->interrupt_in_buffer);
|
kfree(port->interrupt_in_buffer);
|
||||||
kfree(port->interrupt_out_buffer);
|
kfree(port->interrupt_out_buffer);
|
||||||
kfree(port);
|
kfree(port);
|
||||||
|
@ -721,6 +724,7 @@ int usb_serial_probe(struct usb_interface *interface,
|
||||||
unsigned int minor;
|
unsigned int minor;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
int i;
|
int i;
|
||||||
|
int j;
|
||||||
int num_interrupt_in = 0;
|
int num_interrupt_in = 0;
|
||||||
int num_interrupt_out = 0;
|
int num_interrupt_out = 0;
|
||||||
int num_bulk_in = 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) {
|
for (i = 0; i < num_bulk_in; ++i) {
|
||||||
endpoint = bulk_in_endpoint[i];
|
endpoint = bulk_in_endpoint[i];
|
||||||
port = serial->port[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,
|
buffer_size = max_t(int, serial->type->bulk_in_size,
|
||||||
usb_endpoint_maxp(endpoint));
|
usb_endpoint_maxp(endpoint));
|
||||||
port->bulk_in_size = buffer_size;
|
port->bulk_in_size = buffer_size;
|
||||||
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
|
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
|
||||||
port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
|
|
||||||
if (!port->bulk_in_buffer) {
|
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,
|
dev_err(&interface->dev,
|
||||||
"Couldn't allocate bulk_in_buffer\n");
|
"Couldn't allocate bulk_in_buffer\n");
|
||||||
goto probe_error;
|
goto probe_error;
|
||||||
}
|
}
|
||||||
usb_fill_bulk_urb(port->read_urb, dev,
|
usb_fill_bulk_urb(port->read_urbs[j], dev,
|
||||||
usb_rcvbulkpipe(dev,
|
usb_rcvbulkpipe(dev,
|
||||||
endpoint->bEndpointAddress),
|
endpoint->bEndpointAddress),
|
||||||
port->bulk_in_buffer, buffer_size,
|
port->bulk_in_buffers[j], buffer_size,
|
||||||
serial->type->read_bulk_callback, port);
|
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) {
|
for (i = 0; i < num_bulk_out; ++i) {
|
||||||
int j;
|
|
||||||
|
|
||||||
endpoint = bulk_out_endpoint[i];
|
endpoint = bulk_out_endpoint[i];
|
||||||
port = serial->port[i];
|
port = serial->port[i];
|
||||||
if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
|
if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
|
||||||
|
|
|
@ -58,6 +58,9 @@ enum port_dev_state {
|
||||||
* @read_urb: pointer to the bulk in struct urb for this port.
|
* @read_urb: pointer to the bulk in struct urb for this port.
|
||||||
* @bulk_in_endpointAddress: endpoint address for the bulk in pipe for this
|
* @bulk_in_endpointAddress: endpoint address for the bulk in pipe for this
|
||||||
* port.
|
* 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_buffer: pointer to the bulk out buffer for this port.
|
||||||
* @bulk_out_size: the size of the bulk_out_buffer, in bytes.
|
* @bulk_out_size: the size of the bulk_out_buffer, in bytes.
|
||||||
* @write_urb: pointer to the bulk out struct urb for this port.
|
* @write_urb: pointer to the bulk out struct urb for this port.
|
||||||
|
@ -98,6 +101,10 @@ struct usb_serial_port {
|
||||||
struct urb *read_urb;
|
struct urb *read_urb;
|
||||||
__u8 bulk_in_endpointAddress;
|
__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;
|
unsigned char *bulk_out_buffer;
|
||||||
int bulk_out_size;
|
int bulk_out_size;
|
||||||
struct urb *write_urb;
|
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 void usb_serial_generic_release(struct usb_serial *serial);
|
||||||
extern int usb_serial_generic_register(int debug);
|
extern int usb_serial_generic_register(int debug);
|
||||||
extern void usb_serial_generic_deregister(void);
|
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);
|
gfp_t mem_flags);
|
||||||
extern void usb_serial_generic_process_read_urb(struct urb *urb);
|
extern void usb_serial_generic_process_read_urb(struct urb *urb);
|
||||||
extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
|
extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
|
||||||
|
|
Loading…
Reference in New Issue