aboutsummaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_device.c
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2016-09-12 10:23:24 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2016-09-12 10:23:24 +0000
commit2bb33dd7772af050018cc070d814ca817fa006ff (patch)
tree866d143f38922cc516f9cec0a40348f9f03b1b38 /sys/dev/usb/usb_device.c
parent02a0b99cd08756fba07393b2f6abe9d96dffb733 (diff)
downloadsrc-2bb33dd7772af050018cc070d814ca817fa006ff.tar.gz
src-2bb33dd7772af050018cc070d814ca817fa006ff.zip
MFC r305421:
Resolve deadlock between device_detach() and usbd_do_request_flags() by reviving the SX control request lock and refining which lock protects the common scratch area in "struct usb_device". The SX control request lock was removed by r246759 because it caused a lock order reversal with the USB enumeration lock inside usbd_transfer_setup() as a function of r246616. It was thought that reducing the number of locks would resolve the LOR, but because some USB device drivers use usbd_do_request_flags() inside callback functions, like in taskqueues, a deadlock may occur when these are drained from device_detach(). By restoring the SX control request lock usbd_do_request_flags() is allowed to complete its execution when a USB device driver is detaching. By using the SX control request lock to protect the scratch area, the LOR introduced by r246616 is also resolved. Bump the FreeBSD version while at it to force recompilation of all USB kernel modules. Found by: avos@
Notes
Notes: svn path=/stable/8/; revision=305736
Diffstat (limited to 'sys/dev/usb/usb_device.c')
-rw-r--r--sys/dev/usb/usb_device.c44
1 files changed, 40 insertions, 4 deletions
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index 638aef41c9ea..de30122b37a7 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -1550,6 +1550,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
/* initialise our SX-lock */
sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK);
sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS);
+ sx_init_flags(&udev->ctrl_sx, "USB control transfer SX lock", SX_DUPOK);
cv_init(&udev->ctrlreq_cv, "WCTRL");
cv_init(&udev->ref_cv, "UGONE");
@@ -1735,7 +1736,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
*/
/* Protect scratch area */
- do_unlock = usbd_enum_lock(udev);
+ do_unlock = usbd_ctrl_lock(udev);
scratch_ptr = udev->scratch.data;
@@ -1786,7 +1787,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
}
if (do_unlock)
- usbd_enum_unlock(udev);
+ usbd_ctrl_unlock(udev);
/* assume 100mA bus powered for now. Changed when configured. */
udev->power = USB_MIN_POWER;
@@ -2148,6 +2149,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
sx_destroy(&udev->enum_sx);
sx_destroy(&udev->sr_sx);
+ sx_destroy(&udev->ctrl_sx);
cv_destroy(&udev->ctrlreq_cv);
cv_destroy(&udev->ref_cv);
@@ -2311,7 +2313,7 @@ usbd_set_device_strings(struct usb_device *udev)
uint8_t do_unlock;
/* Protect scratch area */
- do_unlock = usbd_enum_lock(udev);
+ do_unlock = usbd_ctrl_lock(udev);
temp_ptr = (char *)udev->scratch.data;
temp_size = sizeof(udev->scratch.data);
@@ -2371,7 +2373,7 @@ usbd_set_device_strings(struct usb_device *udev)
}
if (do_unlock)
- usbd_enum_unlock(udev);
+ usbd_ctrl_unlock(udev);
}
/*
@@ -2853,6 +2855,40 @@ usbd_enum_is_locked(struct usb_device *udev)
}
/*
+ * The following function is used to serialize access to USB control
+ * transfers and the USB scratch area. If the lock is already grabbed
+ * this function returns zero. Else a value of one is returned.
+ */
+uint8_t
+usbd_ctrl_lock(struct usb_device *udev)
+{
+ if (sx_xlocked(&udev->ctrl_sx))
+ return (0);
+ sx_xlock(&udev->ctrl_sx);
+
+ /*
+ * We need to allow suspend and resume at this point, else the
+ * control transfer will timeout if the device is suspended!
+ */
+ if (usbd_enum_is_locked(udev))
+ usbd_sr_unlock(udev);
+ return (1);
+}
+
+void
+usbd_ctrl_unlock(struct usb_device *udev)
+{
+ sx_xunlock(&udev->ctrl_sx);
+
+ /*
+ * Restore the suspend and resume lock after we have unlocked
+ * the USB control transfer lock to avoid LOR:
+ */
+ if (usbd_enum_is_locked(udev))
+ usbd_sr_lock(udev);
+}
+
+/*
* The following function is used to set the per-interface specific
* plug and play information. The string referred to by the pnpinfo
* argument can safely be freed after calling this function. The