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
This commit is contained in:
Josh Gao 2019-09-12 00:03:42 +08:00
parent 983f76b3c6
commit 1025b228ba

View file

@ -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)