aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-01-10 04:15:03 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-15 12:06:57 -0400
commitbe3da032d675cee5156260354182fee760834898 (patch)
treeaca30d00a64dd8979fce5d5aeeaf78e7d5469220 /drivers
parenta5061d0d9c23a6952b7138fa35d0d43523ff22d2 (diff)
USB: Move hcd free_dev call into usb_disconnect to fix oops
commit f7410ced7f931bb1ad79d1336412cf7b7a33cb14 upstream. USB: Move hcd free_dev call into usb_disconnect I found a way to oops the kernel: 1. Open a USB device through devio. 2. Remove the hcd module in the host kernel. 3. Close the devio file descriptor. The problem is that closing the file descriptor does usb_release_dev as it is the last reference. usb_release_dev then tries to invoke the hcd free_dev function (or rather dereferencing the hcd driver struct). This causes an oops as the hcd driver has already been unloaded so the struct is gone. This patch tries to fix this by bringing the free_dev call earlier and into usb_disconnect. I have verified that repeating the above steps no longer crashes with this patch applied. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hcd.h2
-rw-r--r--drivers/usb/core/hub.c12
-rw-r--r--drivers/usb/core/usb.c3
3 files changed, 13 insertions, 4 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index bbe2b924aae8..89613a744236 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -248,7 +248,7 @@ struct hc_driver {
248 /* xHCI specific functions */ 248 /* xHCI specific functions */
249 /* Called by usb_alloc_dev to alloc HC device structures */ 249 /* Called by usb_alloc_dev to alloc HC device structures */
250 int (*alloc_dev)(struct usb_hcd *, struct usb_device *); 250 int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
251 /* Called by usb_release_dev to free HC device structures */ 251 /* Called by usb_disconnect to free HC device structures */
252 void (*free_dev)(struct usb_hcd *, struct usb_device *); 252 void (*free_dev)(struct usb_hcd *, struct usb_device *);
253 253
254 /* Bandwidth computation functions */ 254 /* Bandwidth computation functions */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 35cc8b9ba1f5..9cc0abae7923 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1554,6 +1554,15 @@ static inline void usb_stop_pm(struct usb_device *udev)
1554 1554
1555#endif 1555#endif
1556 1556
1557static void hub_free_dev(struct usb_device *udev)
1558{
1559 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
1560
1561 /* Root hubs aren't real devices, so don't free HCD resources */
1562 if (hcd->driver->free_dev && udev->parent)
1563 hcd->driver->free_dev(hcd, udev);
1564}
1565
1557/** 1566/**
1558 * usb_disconnect - disconnect a device (usbcore-internal) 1567 * usb_disconnect - disconnect a device (usbcore-internal)
1559 * @pdev: pointer to device being disconnected 1568 * @pdev: pointer to device being disconnected
@@ -1624,6 +1633,8 @@ void usb_disconnect(struct usb_device **pdev)
1624 1633
1625 usb_stop_pm(udev); 1634 usb_stop_pm(udev);
1626 1635
1636 hub_free_dev(udev);
1637
1627 put_device(&udev->dev); 1638 put_device(&udev->dev);
1628} 1639}
1629 1640
@@ -3191,6 +3202,7 @@ loop_disable:
3191loop: 3202loop:
3192 usb_ep0_reinit(udev); 3203 usb_ep0_reinit(udev);
3193 release_address(udev); 3204 release_address(udev);
3205 hub_free_dev(udev);
3194 usb_put_dev(udev); 3206 usb_put_dev(udev);
3195 if ((status == -ENOTCONN) || (status == -ENOTSUPP)) 3207 if ((status == -ENOTCONN) || (status == -ENOTSUPP))
3196 break; 3208 break;
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0daff0d968ba..ced0776b9ee9 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -228,9 +228,6 @@ static void usb_release_dev(struct device *dev)
228 hcd = bus_to_hcd(udev->bus); 228 hcd = bus_to_hcd(udev->bus);
229 229
230 usb_destroy_configuration(udev); 230 usb_destroy_configuration(udev);
231 /* Root hubs aren't real devices, so don't free HCD resources */
232 if (hcd->driver->free_dev && udev->parent)
233 hcd->driver->free_dev(hcd, udev);
234 usb_put_hcd(hcd); 231 usb_put_hcd(hcd);
235 kfree(udev->product); 232 kfree(udev->product);
236 kfree(udev->manufacturer); 233 kfree(udev->manufacturer);