summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Mikkel Kamstrup Erlandsen2014-03-20 10:01:53 +0100
committerGravatar Martin Szulecki2014-03-24 17:01:30 +0100
commit8ba560fdd177f107c5a0cf667d4e4ab3b0c59f4a (patch)
tree1d9a1707cab915103074849cc6f5fb8b234f5c20
parentd06ea10f14a806f2770507cd179b62c0d4eda6a2 (diff)
downloadusbmuxd-8ba560fdd177f107c5a0cf667d4e4ab3b0c59f4a.tar.gz
usbmuxd-8ba560fdd177f107c5a0cf667d4e4ab3b0c59f4a.tar.bz2
usb-linux: massive read perf improvement with 3 parallel transfers
By maintaining 3 parallel usb trasfers when reading we get 2-3x more throughput when reading. Without this the usb port is mostly just idling. I get 23mb/s on my system compared to a clean Apple stack that gives me 17mb/s. 3 was chosen because it is simple to hard code, gives very good performance, and have very little impact on out resource consumption.
-rw-r--r--src/usb-linux.c107
1 files changed, 73 insertions, 34 deletions
diff --git a/src/usb-linux.c b/src/usb-linux.c
index 94db8f2..40bf502 100644
--- a/src/usb-linux.c
+++ b/src/usb-linux.c
@@ -4,6 +4,7 @@
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> 4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li> 5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com> 6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com>
7 8
8This program is free software; you can redistribute it and/or modify 9This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by 10it under the terms of the GNU General Public License as published by
@@ -39,6 +40,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
39// we need this because there is currently no asynchronous device discovery mechanism in libusb 40// we need this because there is currently no asynchronous device discovery mechanism in libusb
40#define DEVICE_POLL_TIME 1000 41#define DEVICE_POLL_TIME 1000
41 42
43// Number of parallel bulk transfers we have running for reading data from the device.
44// Older versions of usbmuxd kept only 1, which leads to a mostly dormant USB port.
45// 3 seems to be an all round sensible number - giving better read perf than
46// Apples usbmuxd, at least.
47#define NUM_RX_LOOPS 3
48
42struct usb_device { 49struct usb_device {
43 libusb_device_handle *dev; 50 libusb_device_handle *dev;
44 uint8_t bus, address; 51 uint8_t bus, address;
@@ -46,7 +53,7 @@ struct usb_device {
46 char serial[256]; 53 char serial[256];
47 int alive; 54 int alive;
48 uint8_t interface, ep_in, ep_out; 55 uint8_t interface, ep_in, ep_out;
49 struct libusb_transfer *rx_xfer; 56 struct collection rx_xfers;
50 struct collection tx_xfers; 57 struct collection tx_xfers;
51 int wMaxPacketSize; 58 int wMaxPacketSize;
52}; 59};
@@ -64,17 +71,20 @@ static void usb_disconnect(struct usb_device *dev)
64 return; 71 return;
65 } 72 }
66 73
67 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device 74 // kill the rx xfer and tx xfers and try to make sure the callbacks
68 if(dev->rx_xfer) { 75 // get called before we free the device
69 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer"); 76 FOREACH(struct libusb_transfer *xfer, &dev->rx_xfers) {
70 libusb_cancel_transfer(dev->rx_xfer); 77 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer %p", xfer);
71 } 78 libusb_cancel_transfer(xfer);
79 } ENDFOREACH
80
72 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) { 81 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
73 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer); 82 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
74 libusb_cancel_transfer(xfer); 83 libusb_cancel_transfer(xfer);
75 } ENDFOREACH 84 } ENDFOREACH
76 85
77 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) { 86 // Busy-wait until all xfers are closed
87 while(collection_count(&dev->rx_xfers) || collection_count(&dev->tx_xfers)) {
78 struct timeval tv; 88 struct timeval tv;
79 int res; 89 int res;
80 90
@@ -85,7 +95,9 @@ static void usb_disconnect(struct usb_device *dev)
85 break; 95 break;
86 } 96 }
87 } 97 }
98
88 collection_free(&dev->tx_xfers); 99 collection_free(&dev->tx_xfers);
100 collection_free(&dev->rx_xfers);
89 libusb_release_interface(dev->dev, dev->interface); 101 libusb_release_interface(dev->dev, dev->interface);
90 libusb_close(dev->dev); 102 libusb_close(dev->dev);
91 dev->dev = NULL; 103 dev->dev = NULL;
@@ -93,6 +105,15 @@ static void usb_disconnect(struct usb_device *dev)
93 free(dev); 105 free(dev);
94} 106}
95 107
108static void reap_dead_devices(void) {
109 FOREACH(struct usb_device *usbdev, &device_list) {
110 if(!usbdev->alive) {
111 device_remove(usbdev);
112 usb_disconnect(usbdev);
113 }
114 } ENDFOREACH
115}
116
96// Callback from write operation 117// Callback from write operation
97static void tx_callback(struct libusb_transfer *xfer) 118static void tx_callback(struct libusb_transfer *xfer)
98{ 119{
@@ -201,9 +222,11 @@ static void rx_callback(struct libusb_transfer *xfer)
201 // this should never be reached. 222 // this should never be reached.
202 break; 223 break;
203 } 224 }
225
204 free(xfer->buffer); 226 free(xfer->buffer);
205 dev->rx_xfer = NULL; 227 collection_remove(&dev->rx_xfers, xfer);
206 libusb_free_transfer(xfer); 228 libusb_free_transfer(xfer);
229
207 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events 230 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
208 // we'll do device_remove there too 231 // we'll do device_remove there too
209 dev->alive = 0; 232 dev->alive = 0;
@@ -211,19 +234,21 @@ static void rx_callback(struct libusb_transfer *xfer)
211} 234}
212 235
213// Start a read-callback loop for this device 236// Start a read-callback loop for this device
214static int start_rx(struct usb_device *dev) 237static int start_rx_loop(struct usb_device *dev)
215{ 238{
216 int res; 239 int res;
217 void *buf; 240 void *buf;
218 dev->rx_xfer = libusb_alloc_transfer(0); 241 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
219 buf = malloc(USB_MRU); 242 buf = malloc(USB_MRU);
220 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0); 243 libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0);
221 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) { 244 if((res = libusb_submit_transfer(xfer)) != 0) {
222 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res); 245 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
223 libusb_free_transfer(dev->rx_xfer); 246 libusb_free_transfer(xfer);
224 dev->rx_xfer = NULL;
225 return res; 247 return res;
226 } 248 }
249
250 collection_add(&dev->rx_xfers, xfer);
251
227 return 0; 252 return 0;
228} 253}
229 254
@@ -253,10 +278,14 @@ int usb_discover(void)
253 278
254 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt); 279 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
255 280
281 // Mark all devices as dead, and do a mark-sweep like
282 // collection of dead devices
256 FOREACH(struct usb_device *usbdev, &device_list) { 283 FOREACH(struct usb_device *usbdev, &device_list) {
257 usbdev->alive = 0; 284 usbdev->alive = 0;
258 } ENDFOREACH 285 } ENDFOREACH
259 286
287 // Enumerate all USB devices and mark the ones we already know
288 // about as live, again
260 for(i=0; i<cnt; i++) { 289 for(i=0; i<cnt; i++) {
261 // the following are non-blocking operations on the device list 290 // the following are non-blocking operations on the device list
262 libusb_device *dev = devs[i]; 291 libusb_device *dev = devs[i];
@@ -407,6 +436,7 @@ int usb_discover(void)
407 } 436 }
408 437
409 collection_init(&usbdev->tx_xfers); 438 collection_init(&usbdev->tx_xfers);
439 collection_init(&usbdev->rx_xfers);
410 440
411 collection_add(&device_list, usbdev); 441 collection_add(&device_list, usbdev);
412 442
@@ -414,19 +444,37 @@ int usb_discover(void)
414 usb_disconnect(usbdev); 444 usb_disconnect(usbdev);
415 continue; 445 continue;
416 } 446 }
417 if(start_rx(usbdev) < 0) { 447
448 // Spin up NUM_RX_LOOPS parallel usb data retrieval loops
449 // Old usbmuxds used only 1 rx loop, but that leaves the
450 // USB port sleeping most of the time
451 int rx_loops = NUM_RX_LOOPS;
452 for (rx_loops = NUM_RX_LOOPS; rx_loops > 0; rx_loops--) {
453 if(start_rx_loop(usbdev) < 0) {
454 usbmuxd_log(LL_WARNING, "Failed to start RX loop number %d", NUM_RX_LOOPS - rx_loops);
455 }
456 }
457
458 // Ensure we have at least 1 RX loop going
459 if (rx_loops == NUM_RX_LOOPS) {
460 usbmuxd_log(LL_FATAL, "Failed to start any RX loop for device %d-%d",
461 usbdev->bus, usbdev->address);
418 device_remove(usbdev); 462 device_remove(usbdev);
419 usb_disconnect(usbdev); 463 usb_disconnect(usbdev);
420 continue; 464 continue;
465 } else if (rx_loops > 0) {
466 usbmuxd_log(LL_WARNING, "Failed to start all %d RX loops. Going on with %d loops. "
467 "This may have negative impact on device read speed.",
468 NUM_RX_LOOPS, NUM_RX_LOOPS - rx_loops);
469 } else {
470 usbmuxd_log(LL_DEBUG, "All %d RX loops started successfully", NUM_RX_LOOPS);
421 } 471 }
472
422 valid_count++; 473 valid_count++;
423 } 474 }
424 FOREACH(struct usb_device *usbdev, &device_list) { 475
425 if(!usbdev->alive) { 476 // Clean out any device we didn't mark back as live
426 device_remove(usbdev); 477 reap_dead_devices();
427 usb_disconnect(usbdev);
428 }
429 } ENDFOREACH
430 478
431 libusb_free_device_list(devs, 1); 479 libusb_free_device_list(devs, 1);
432 480
@@ -530,13 +578,9 @@ int usb_process(void)
530 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); 578 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
531 return res; 579 return res;
532 } 580 }
581
533 // reap devices marked dead due to an RX error 582 // reap devices marked dead due to an RX error
534 FOREACH(struct usb_device *usbdev, &device_list) { 583 reap_dead_devices();
535 if(!usbdev->alive) {
536 device_remove(usbdev);
537 usb_disconnect(usbdev);
538 }
539 } ENDFOREACH
540 584
541 if(dev_poll_remain_ms() <= 0) { 585 if(dev_poll_remain_ms() <= 0) {
542 res = usb_discover(); 586 res = usb_discover();
@@ -570,13 +614,8 @@ int usb_process_timeout(int msec)
570 return res; 614 return res;
571 } 615 }
572 // reap devices marked dead due to an RX error 616 // reap devices marked dead due to an RX error
573 FOREACH(struct usb_device *usbdev, &device_list) { 617 reap_dead_devices();
574 if(!usbdev->alive) { 618 gettimeofday(&tcur, NULL);
575 device_remove(usbdev);
576 usb_disconnect(usbdev);
577 }
578 } ENDFOREACH
579 gettimeofday(&tcur, NULL);
580 } 619 }
581 return 0; 620 return 0;
582} 621}