aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial/generic.c
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2010-05-05 17:57:37 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-05-20 16:21:42 -0400
commit27c7acf22047fbe4ec4cc36b7c2610dba227697c (patch)
treec0a8f217fc2d7a302b4d2e084bb126e825006ca4 /drivers/usb/serial/generic.c
parent4272568b3dd8dbad36014a107c0fbbef6400c917 (diff)
USB: serial: reimplement generic fifo-based writes
Reimplement fifo-based writes in the generic driver using a multiple pre-allocated urb scheme. In contrast to multi-urb writes, no allocations (of urbs or buffers) are made during run-time and there is less pressure on the host stack queues as currently only two urbs are used (implementation is generic and can handle more than two urbs as well, though). Initial tests using ftdi_sio show that the implementation achieves the same (maximum) throughput at high baudrates as multi-urb writes. The CPU usage is much lower than for multi-urb writes for small write requests and only slightly higher for large (e.g. 2k) requests (due to extra copy via fifo?). Also outperforms multi-urb writes for small write requests on an embedded arm-9 system, where multi-urb writes are CPU-bound at high baudrates (perf reveals that a lot of time is spent in the host stack enqueue function -- could perhaps be a bug as well). Keeping the original write_urb, buffer and flag for now as there are other drivers depending on them. Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/generic.c')
-rw-r--r--drivers/usb/serial/generic.c59
1 files changed, 39 insertions, 20 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 1a134f9c64f3..3ae17840175c 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -1,6 +1,7 @@
1/* 1/*
2 * USB Serial Converter Generic functions 2 * USB Serial Converter Generic functions
3 * 3 *
4 * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com)
4 * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) 5 * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
5 * 6 *
6 * This program is free software; you can redistribute it and/or 7 * This program is free software; you can redistribute it and/or
@@ -143,6 +144,7 @@ static void generic_cleanup(struct usb_serial_port *port)
143{ 144{
144 struct usb_serial *serial = port->serial; 145 struct usb_serial *serial = port->serial;
145 unsigned long flags; 146 unsigned long flags;
147 int i;
146 148
147 dbg("%s - port %d", __func__, port->number); 149 dbg("%s - port %d", __func__, port->number);
148 150
@@ -150,6 +152,8 @@ static void generic_cleanup(struct usb_serial_port *port)
150 /* shutdown any bulk transfers that might be going on */ 152 /* shutdown any bulk transfers that might be going on */
151 if (port->bulk_out_size) { 153 if (port->bulk_out_size) {
152 usb_kill_urb(port->write_urb); 154 usb_kill_urb(port->write_urb);
155 for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
156 usb_kill_urb(port->write_urbs[i]);
153 157
154 spin_lock_irqsave(&port->lock, flags); 158 spin_lock_irqsave(&port->lock, flags);
155 kfifo_reset_out(&port->write_fifo); 159 kfifo_reset_out(&port->write_fifo);
@@ -258,46 +262,56 @@ err_urb:
258 * usb_serial_generic_write_start - kick off an URB write 262 * usb_serial_generic_write_start - kick off an URB write
259 * @port: Pointer to the &struct usb_serial_port data 263 * @port: Pointer to the &struct usb_serial_port data
260 * 264 *
261 * Returns the number of bytes queued on success. This will be zero if there 265 * Returns zero on success, or a negative errno value
262 * was nothing to send. Otherwise, it returns a negative errno value
263 */ 266 */
264static int usb_serial_generic_write_start(struct usb_serial_port *port) 267static int usb_serial_generic_write_start(struct usb_serial_port *port)
265{ 268{
266 int result; 269 struct urb *urb;
267 int count; 270 int count, result;
268 unsigned long flags; 271 unsigned long flags;
272 int i;
269 273
274 if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
275 return 0;
276retry:
270 spin_lock_irqsave(&port->lock, flags); 277 spin_lock_irqsave(&port->lock, flags);
271 if (port->write_urb_busy || !kfifo_len(&port->write_fifo)) { 278 if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
279 clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
272 spin_unlock_irqrestore(&port->lock, flags); 280 spin_unlock_irqrestore(&port->lock, flags);
273 return 0; 281 return 0;
274 } 282 }
275 port->write_urb_busy = 1; 283 i = (int)find_first_bit(&port->write_urbs_free,
284 ARRAY_SIZE(port->write_urbs));
276 spin_unlock_irqrestore(&port->lock, flags); 285 spin_unlock_irqrestore(&port->lock, flags);
277 286
287 urb = port->write_urbs[i];
278 count = port->serial->type->prepare_write_buffer(port, 288 count = port->serial->type->prepare_write_buffer(port,
279 &port->write_urb->transfer_buffer, 289 &urb->transfer_buffer,
280 port->bulk_out_size, NULL, 0); 290 port->bulk_out_size, NULL, 0);
281 usb_serial_debug_data(debug, &port->dev, __func__, 291 urb->transfer_buffer_length = count;
282 count, port->write_urb->transfer_buffer); 292 usb_serial_debug_data(debug, &port->dev, __func__, count,
283 port->write_urb->transfer_buffer_length = count; 293 urb->transfer_buffer);
284 294 result = usb_submit_urb(urb, GFP_ATOMIC);
285 /* send the data out the bulk port */
286 result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
287 if (result) { 295 if (result) {
288 dev_err(&port->dev, "%s - error submitting urb: %d\n", 296 dev_err(&port->dev, "%s - error submitting urb: %d\n",
289 __func__, result); 297 __func__, result);
290 /* don't have to grab the lock here, as we will 298 clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
291 retry if != 0 */
292 port->write_urb_busy = 0;
293 return result; 299 return result;
294 } 300 }
301 clear_bit(i, &port->write_urbs_free);
295 302
296 spin_lock_irqsave(&port->lock, flags); 303 spin_lock_irqsave(&port->lock, flags);
297 port->tx_bytes += count; 304 port->tx_bytes += count;
298 spin_unlock_irqrestore(&port->lock, flags); 305 spin_unlock_irqrestore(&port->lock, flags);
299 306
300 return count; 307 /* Try sending off another urb, unless in irq context (in which case
308 * there will be no free urb). */
309 if (!in_irq())
310 goto retry;
311
312 clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
313
314 return 0;
301} 315}
302 316
303/** 317/**
@@ -461,6 +475,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
461 unsigned long flags; 475 unsigned long flags;
462 struct usb_serial_port *port = urb->context; 476 struct usb_serial_port *port = urb->context;
463 int status = urb->status; 477 int status = urb->status;
478 int i;
464 479
465 dbg("%s - port %d", __func__, port->number); 480 dbg("%s - port %d", __func__, port->number);
466 481
@@ -472,9 +487,13 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
472 port->tx_urbs--; 487 port->tx_urbs--;
473 spin_unlock_irqrestore(&port->lock, flags); 488 spin_unlock_irqrestore(&port->lock, flags);
474 } else { 489 } else {
490 for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
491 if (port->write_urbs[i] == urb)
492 break;
493
475 spin_lock_irqsave(&port->lock, flags); 494 spin_lock_irqsave(&port->lock, flags);
476 port->tx_bytes -= urb->transfer_buffer_length; 495 port->tx_bytes -= urb->transfer_buffer_length;
477 port->write_urb_busy = 0; 496 set_bit(i, &port->write_urbs_free);
478 spin_unlock_irqrestore(&port->lock, flags); 497 spin_unlock_irqrestore(&port->lock, flags);
479 498
480 if (status) { 499 if (status) {
@@ -576,7 +595,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
576 c++; 595 c++;
577 } 596 }
578 597
579 if (port->write_urb) { 598 if (port->bulk_out_size) {
580 r = usb_serial_generic_write_start(port); 599 r = usb_serial_generic_write_start(port);
581 if (r < 0) 600 if (r < 0)
582 c++; 601 c++;