aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-06-13 22:37:29 +0200
committerHans de Goede <hdegoede@redhat.com>2013-06-19 11:04:15 +0200
commit08befee03cff76f62918a82d92696aa84d01515c (patch)
tree05e75b397dc2a883587775c2c86b9dacfc4027ef
parentd4e993192aa81a1867bddf90d53cf750ec21e7b1 (diff)
downloadandroid_external_libusbx-08befee03cff76f62918a82d92696aa84d01515c.tar.gz
android_external_libusbx-08befee03cff76f62918a82d92696aa84d01515c.tar.bz2
android_external_libusbx-08befee03cff76f62918a82d92696aa84d01515c.zip
linux_usbfs: Work around a driver binding race in reset handling
I've been seeing these intermittent failures to reclaim an interface after a device reset. After much debugging and inserting sleeps in strategic places to make the race window larger I've found the following race: 1) A user is running some software using libusbx which will automatically detect, and "bind" to, any newly plugged in USB-devices. For example a virtual machine viewer with automatic USB-redirection 2) The user plugs in a new usb-storage device 3) The usb-storage driver is not yet loaded, udev spawns "modprobe usb-storage", this blocks on disk-io 4) The libusbx app opens the device, claims all interfaces, does a device-reset 5) While the IOCTL_USBFS_RESET is running the modprobe completes 6) The driver registration blocks on an USB lock held by the reset code path 7) When the reset finishes the driver registration completes and the driver binds itself to the device, before IOCTL_USBFS_RESET returns to userspace 8) libusbx tries to re-claim all interfaces it had claimed before the reset 9) libusbx fails as usb-storage is now bound to it This patch works around this issue by simply unbinding the driver for all interfaces which were claimed before the reset. Normally this is a no-op as no driver (other then usbfs) can be bound for claimed interfaces before the reset. This patch also improves the error logging, and makes libusb_device_reset properly return an error when re-claiming fails. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--libusb/os/linux_usbfs.c12
-rw-r--r--libusb/version_nano.h2
2 files changed, 11 insertions, 3 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 6a4a3f1..3d21a27 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -127,6 +127,7 @@ static int linux_start_event_monitor(void);
static int linux_stop_event_monitor(void);
static int linux_scan_devices(struct libusb_context *ctx);
static int sysfs_scan_device(struct libusb_context *ctx, const char *devname);
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int);
#if !defined(USE_UDEV)
static int linux_default_scan_devices (struct libusb_context *ctx);
@@ -1428,11 +1429,18 @@ static int op_reset_device(struct libusb_device_handle *handle)
/* And re-claim any interfaces which were claimed before the reset */
for (i = 0; i < USB_MAXINTERFACES; i++) {
if (handle->claimed_interfaces & (1L << i)) {
- r = claim_interface(handle, i);
+ /*
+ * A driver may have completed modprobing during
+ * IOCTL_USBFS_RESET, and bound itself as soon as
+ * IOCTL_USBFS_RESET released the device lock
+ */
+ r = detach_kernel_driver_and_claim(handle, i);
if (r) {
usbi_warn(HANDLE_CTX(handle),
- "failed to re-claim interface %d after reset", i);
+ "failed to re-claim interface %d after reset: %s",
+ i, libusb_error_name(r));
handle->claimed_interfaces &= ~(1L << i);
+ ret = LIBUSB_ERROR_NOT_FOUND;
}
}
}
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 42b9c0e..24d4bb2 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10743
+#define LIBUSB_NANO 10744