svn commit: r257206 - head/sys/dev/usb

Hans Petter Selasky hselasky at FreeBSD.org
Sun Oct 27 10:09:55 UTC 2013


Author: hselasky
Date: Sun Oct 27 10:09:53 2013
New Revision: 257206
URL: http://svnweb.freebsd.org/changeset/base/257206

Log:
  Fix a deadlock when trying to power off a USB device. The deadlock
  happens because the code in question is trying to modify the parent
  USB port registers outside the USB explore thread.
  
  MFC after:	3 days

Modified:
  head/sys/dev/usb/usb_dev.c
  head/sys/dev/usb/usb_device.h
  head/sys/dev/usb/usb_generic.c
  head/sys/dev/usb/usb_hub.c

Modified: head/sys/dev/usb/usb_dev.c
==============================================================================
--- head/sys/dev/usb/usb_dev.c	Sun Oct 27 04:59:18 2013	(r257205)
+++ head/sys/dev/usb/usb_dev.c	Sun Oct 27 10:09:53 2013	(r257206)
@@ -1099,7 +1099,7 @@ usb_ioctl(struct cdev *dev, u_long cmd, 
 
 	/* Wait for re-enumeration, if any */
 
-	while (f->udev->re_enumerate_wait != 0) {
+	while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) {
 
 		usb_unref_device(cpd, &refs);
 

Modified: head/sys/dev/usb/usb_device.h
==============================================================================
--- head/sys/dev/usb/usb_device.h	Sun Oct 27 04:59:18 2013	(r257205)
+++ head/sys/dev/usb/usb_device.h	Sun Oct 27 10:09:53 2013	(r257206)
@@ -238,6 +238,9 @@ struct usb_device {
 	uint8_t	driver_added_refcount;	/* our driver added generation count */
 	uint8_t	power_mode;		/* see USB_POWER_XXX */
 	uint8_t re_enumerate_wait;	/* set if re-enum. is in progress */
+#define	USB_RE_ENUM_DONE	0
+#define	USB_RE_ENUM_START	1
+#define	USB_RE_ENUM_PWR_OFF	2
 	uint8_t ifaces_max;		/* number of interfaces present */
 	uint8_t endpoints_max;		/* number of endpoints present */
 

Modified: head/sys/dev/usb/usb_generic.c
==============================================================================
--- head/sys/dev/usb/usb_generic.c	Sun Oct 27 04:59:18 2013	(r257205)
+++ head/sys/dev/usb/usb_generic.c	Sun Oct 27 10:09:53 2013	(r257206)
@@ -1762,16 +1762,11 @@ ugen_set_power_mode(struct usb_fifo *f, 
 
 	switch (mode) {
 	case USB_POWER_MODE_OFF:
-		/* get the device unconfigured */
-		err = ugen_set_config(f, USB_UNCONFIG_INDEX);
-		if (err) {
-			DPRINTFN(0, "Could not unconfigure "
-			    "device (ignored)\n");
+		if (udev->flags.usb_mode == USB_MODE_HOST &&
+		    udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+			udev->re_enumerate_wait = USB_RE_ENUM_PWR_OFF;
 		}
-
-		/* clear port enable */
-		err = usbd_req_clear_port_feature(udev->parent_hub,
-		    NULL, udev->port_no, UHF_PORT_ENABLE);
+		/* set power mode will wake up the explore thread */
 		break;
 
 	case USB_POWER_MODE_ON:
@@ -1819,9 +1814,9 @@ ugen_set_power_mode(struct usb_fifo *f, 
 
 	/* if we are powered off we need to re-enumerate first */
 	if (old_mode == USB_POWER_MODE_OFF) {
-		if (udev->flags.usb_mode == USB_MODE_HOST) {
-			if (udev->re_enumerate_wait == 0)
-				udev->re_enumerate_wait = 1;
+		if (udev->flags.usb_mode == USB_MODE_HOST &&
+		    udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+			udev->re_enumerate_wait = USB_RE_ENUM_START;
 		}
 		/* set power mode will wake up the explore thread */
 	}

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c	Sun Oct 27 04:59:18 2013	(r257205)
+++ head/sys/dev/usb/usb_hub.c	Sun Oct 27 10:09:53 2013	(r257206)
@@ -248,7 +248,8 @@ uhub_explore_sub(struct uhub_softc *sc, 
 		uint8_t do_unlock;
 		
 		do_unlock = usbd_enum_lock(child);
-		if (child->re_enumerate_wait) {
+		switch (child->re_enumerate_wait) {
+		case USB_RE_ENUM_START:
 			err = usbd_set_config_index(child,
 			    USB_UNCONFIG_INDEX);
 			if (err != 0) {
@@ -263,8 +264,33 @@ uhub_explore_sub(struct uhub_softc *sc, 
 				err = usb_probe_and_attach(child,
 				    USB_IFACE_INDEX_ANY);
 			}
-			child->re_enumerate_wait = 0;
+			child->re_enumerate_wait = USB_RE_ENUM_DONE;
 			err = 0;
+			break;
+
+		case USB_RE_ENUM_PWR_OFF:
+			/* get the device unconfigured */
+			err = usbd_set_config_index(child,
+			    USB_UNCONFIG_INDEX);
+			if (err) {
+				DPRINTFN(0, "Could not unconfigure "
+				    "device (ignored)\n");
+			}
+
+			/* clear port enable */
+			err = usbd_req_clear_port_feature(child->parent_hub,
+			    NULL, child->port_no, UHF_PORT_ENABLE);
+			if (err) {
+				DPRINTFN(0, "Could not disable port "
+				    "(ignored)\n");
+			}
+			child->re_enumerate_wait = USB_RE_ENUM_DONE;
+			err = 0;
+			break;
+
+		default:
+			child->re_enumerate_wait = USB_RE_ENUM_DONE;
+			break;
 		}
 		if (do_unlock)
 			usbd_enum_unlock(child);
@@ -2086,7 +2112,7 @@ usb_peer_should_wakeup(struct usb_device
 	return (((udev->power_mode == USB_POWER_MODE_ON) &&
 	    (udev->flags.usb_mode == USB_MODE_HOST)) ||
 	    (udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
-	    (udev->re_enumerate_wait != 0) ||
+	    (udev->re_enumerate_wait != USB_RE_ENUM_DONE) ||
 	    (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
 	    (udev->pwr_save.write_refs != 0) ||
 	    ((udev->pwr_save.read_refs != 0) &&
@@ -2502,6 +2528,8 @@ usbd_set_power_mode(struct usb_device *u
 
 #if USB_HAVE_POWERD
 	usb_bus_power_update(udev->bus);
+#else
+	usb_needs_explore(udev->bus, 0 /* no probe */ );
 #endif
 }
 
@@ -2540,8 +2568,8 @@ usbd_filter_power_mode(struct usb_device
 void
 usbd_start_re_enumerate(struct usb_device *udev)
 {
-	if (udev->re_enumerate_wait == 0) {
-		udev->re_enumerate_wait = 1;
+	if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+		udev->re_enumerate_wait = USB_RE_ENUM_START;
 		usb_needs_explore(udev->bus, 0);
 	}
 }


More information about the svn-src-head mailing list