summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_device.c
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2014-01-24 08:10:08 +0000
committerhselasky <hselasky@FreeBSD.org>2014-01-24 08:10:08 +0000
commit6e5156d6bb6755f251e480703c56db26ea69c02a (patch)
tree7e6f2f512b9261046f389034ef2aafa0d782d0e8 /sys/dev/usb/usb_device.c
parentf692196495136a8dd592a7f06a940e6fae03fd6d (diff)
downloadFreeBSD-src-6e5156d6bb6755f251e480703c56db26ea69c02a.zip
FreeBSD-src-6e5156d6bb6755f251e480703c56db26ea69c02a.tar.gz
MFC r260808 and r260814:
- Close a minor deadlock. - Fix a possible memory use after free and leak situation associated with USB device detach when using character device handles. This also includes LibUSB. It turns out that "usb_close()" cannot always get a reference to clean up its USB transfers and such, if called during the kernel USB device detach.
Diffstat (limited to 'sys/dev/usb/usb_device.c')
-rw-r--r--sys/dev/usb/usb_device.c24
1 files changed, 14 insertions, 10 deletions
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index 3af48d8..76d493c 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -2070,6 +2070,8 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no);
bus = udev->bus;
+
+ /* set DETACHED state to prevent any further references */
usb_set_device_state(udev, USB_STATE_DETACHED);
#if USB_HAVE_DEVCTL
@@ -2085,16 +2087,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
usb_free_symlink(udev->ugen_symlink);
udev->ugen_symlink = NULL;
}
-#endif
- /*
- * Unregister our device first which will prevent any further
- * references:
- */
- usb_bus_port_set_device(bus, udev->parent_hub ?
- udev->parent_hub->hub->ports + udev->port_index : NULL,
- NULL, USB_ROOT_HUB_ADDR);
-#if USB_HAVE_UGEN
/* wait for all pending references to go away: */
mtx_lock(&usb_ref_lock);
udev->refcount--;
@@ -2114,6 +2107,11 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
/* the following will get the device unconfigured in software */
usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0);
+ /* final device unregister after all character devices are closed */
+ usb_bus_port_set_device(bus, udev->parent_hub ?
+ udev->parent_hub->hub->ports + udev->port_index : NULL,
+ NULL, USB_ROOT_HUB_ADDR);
+
/* unsetup any leftover default USB transfers */
usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX);
@@ -2647,8 +2645,14 @@ usb_set_device_state(struct usb_device *udev, enum usb_dev_state state)
DPRINTF("udev %p state %s -> %s\n", udev,
usb_statestr(udev->state), usb_statestr(state));
- udev->state = state;
+#if USB_HAVE_UGEN
+ mtx_lock(&usb_ref_lock);
+#endif
+ udev->state = state;
+#if USB_HAVE_UGEN
+ mtx_unlock(&usb_ref_lock);
+#endif
if (udev->bus->methods->device_state_change != NULL)
(udev->bus->methods->device_state_change) (udev);
}
OpenPOWER on IntegriCloud