Re: [PATCH] usb/core: Fix race condition when removing EHCI PCI devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 10 Sep 2012, Don Zickus wrote:

> A customer of ours noticed that after a bunch of hot removals of the EHCI
> PCI device, a panic occurs. This happened on a 2.6.32 RHEL-6 kernel, but
> I believe is still applicable upstream.
> 
> The panic was further simplified to enabling SLUB debug poisoning and running
> the following command:
> 
> while true; do find /proc/bus/usb -type f -exec cat {} >/dev/null \; ; done &
> 
> This gets the machine to panic after 1 or 2 removals of the device.

> This is likely because the following thread was in the process of removing the
> device:

> The fix that solved their problem was to deregister the usb bus first before
> running usb_put_dev.  After running multiple tests the panic disappeared.
> 
> Deregistering the bus first to prevent future readers from accessing the device
> before cleaning up the hcd, seemed like an appropriate way to go.  I'll leave
> it to the experts to validate that or provide a better solution. :-)
> 
> I adapted their usb_remove_hcd fix for usb_add_hcd error path too to mimic
> the same behaviour (a little code shuffling to handle the gotos cleanly).

Moving things around in usb_add_hcd() is not a good way to attack this
problem.  Here's a better approach; please make sure that it does what
you want.  The idea is to indicate that a root hub is not registered by
setting its devnum field to 0.

Alan Stern



Index: usb-3.6/drivers/usb/core/devices.c
===================================================================
--- usb-3.6.orig/drivers/usb/core/devices.c
+++ usb-3.6/drivers/usb/core/devices.c
@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct fi
 	/* print devices for all busses */
 	list_for_each_entry(bus, &usb_bus_list, bus_list) {
 		/* recurse through all children of the root hub */
-		if (!bus->root_hub)
+		if (!bus->root_hub || bus->root_hub->devnum == 0)
 			continue;
 		usb_lock_device(bus->root_hub);
 		ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
Index: usb-3.6/drivers/usb/core/hcd.c
===================================================================
--- usb-3.6.orig/drivers/usb/core/hcd.c
+++ usb-3.6/drivers/usb/core/hcd.c
@@ -980,6 +980,8 @@ static int register_root_hub(struct usb_
 	const int devnum = 1;
 	int retval;
 
+	mutex_lock(&usb_bus_list_lock);
+
 	usb_dev->devnum = devnum;
 	usb_dev->bus->devnum_next = devnum + 1;
 	memset (&usb_dev->bus->devmap.devicemap, 0,
@@ -987,8 +989,6 @@ static int register_root_hub(struct usb_
 	set_bit (devnum, usb_dev->bus->devmap.devicemap);
 	usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
 
-	mutex_lock(&usb_bus_list_lock);
-
 	usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
 	retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
 	if (retval != sizeof usb_dev->descriptor) {
@@ -1011,6 +1011,7 @@ static int register_root_hub(struct usb_
 	if (retval) {
 		dev_err (parent_dev, "can't register root hub for %s, %d\n",
 				dev_name(&usb_dev->dev), retval);
+		usb_dev->devnum = 0;
 	}
 	mutex_unlock(&usb_bus_list_lock);
 
@@ -2527,6 +2528,7 @@ error_create_attr_group:
 #endif
 	mutex_lock(&usb_bus_list_lock);
 	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */
+	hcd->self.root_hub->devnum = 0;
 	mutex_unlock(&usb_bus_list_lock);
 err_register_root_hub:
 	hcd->rh_pollable = 0;
@@ -2583,6 +2585,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 
 	mutex_lock(&usb_bus_list_lock);
 	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */
+	hcd->self.root_hub->devnum = 0;
 	mutex_unlock(&usb_bus_list_lock);
 
 	/* Prevent any more root-hub status calls from the timer.

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux