mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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
					
				
					 3 changed files with 101 additions and 41 deletions
				
			
		| 
						 | 
					@ -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) {
 | 
				
			||||||
			dev_err(&interface->dev,
 | 
								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");
 | 
										"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 a new issue