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:
parent
983f76b3c6
commit
1025b228ba
1 changed files with 26 additions and 7 deletions
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue