From 1025b228bab33e82f2743b6316b16e85ac2b41d6 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Thu, 12 Sep 2019 00:03:42 +0800 Subject: [PATCH] adb: fragment host linux USB writes when needed. We've seen USB writes failing due to inability to allocate contiguous chunks of memory in the kernel on devices, but it looks like the same problem can occur on the host, as well. It's a mild performance regression (90->80 MB/s on a blueline) to split the writes always, so attempt the full write first, and fall back to splitting it up if that fails with ENOMEM. Once we switch over the the asynchronous transport API, we'll be able to submit multiple writes cheaply, like on devices, so we won't need to retry at that point. Bug: http://b/140985544 Test: test_device.py Change-Id: I1517c348375b829dfff6796c4e9d394802b02d5b --- adb/client/usb_linux.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp index 17b4db159..24e722eb9 100644 --- a/adb/client/usb_linux.cpp +++ b/adb/client/usb_linux.cpp @@ -406,25 +406,44 @@ static int usb_bulk_read(usb_handle* h, void* data, int len) { } } -int usb_write(usb_handle *h, const void *_data, int len) -{ +static int usb_write_split(usb_handle* h, unsigned char* data, int len) { + for (int i = 0; i < len; i += 16384) { + int chunk_size = (i + 16384 > len) ? len - i : 16384; + int n = usb_bulk_write(h, data + i, chunk_size); + if (n != chunk_size) { + D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno)); + return -1; + } + } + + return len; +} + +int usb_write(usb_handle* h, const void* _data, int len) { D("++ usb_write ++"); - unsigned char *data = (unsigned char*) _data; + unsigned char* data = (unsigned char*)_data; + + // The kernel will attempt to allocate a contiguous buffer for each write we submit. + // This might fail due to heap fragmentation, so attempt a contiguous write once, and if that + // fails, retry after having split the data into 16kB chunks to avoid allocation failure. int n = usb_bulk_write(h, data, len); - if (n != len) { - D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno)); + if (n == -1 && errno == ENOMEM) { + n = usb_write_split(h, data, len); + } + + if (n == -1) { return -1; } if (h->zero_mask && !(len & h->zero_mask)) { // If we need 0-markers and our transfer is an even multiple of the packet size, // then send a zero marker. - return usb_bulk_write(h, _data, 0) == 0 ? n : -1; + return usb_bulk_write(h, _data, 0) == 0 ? len : -1; } D("-- usb_write --"); - return n; + return len; } int usb_read(usb_handle *h, void *_data, int len)