aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAndy Whitcroft <apw@canonical.com>2010-02-17 10:13:42 +0000
committerLeann Ogasawara <leann.ogasawara@canonical.com>2012-06-18 07:01:05 -0700
commitddaa230ccd028cfabcec87391b59f762eb2f8a64 (patch)
tree3e7f6fd9c65635eac49ad6df42593cdd52d54ac8 /drivers
parent053acc19f6dcd4272c0f3f562e7fc80c3bb79430 (diff)
UBUNTU: SAUCE: khubd -- switch USB product/manufacturer/serial handling to RCU
BugLink: http://bugs.launchpad.net/bugs/510937 With the introduction of wireless USB hubs the product, manufacturer, and serial number are now mutable. This necessitates new locking in the consumers of these values including the sysfs read routines in order to prevent use-after-free acces to these values. These extra locks create significant lock contention leading to increased boot times (0.3s for an example Atom based system). Move update of these values to RCU based locking. Signed-off-by: Andy Whitcroft <apw@canonical.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c34
-rw-r--r--drivers/usb/core/sysfs.c6
2 files changed, 31 insertions, 9 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 25a7422ee65..ce8a9576434 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -24,6 +24,7 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -2218,6 +2219,10 @@ fail:
*/
int usb_deauthorize_device(struct usb_device *usb_dev)
{
+ char *product = NULL;
+ char *manufacturer = NULL;
+ char *serial = NULL;
+
usb_lock_device(usb_dev);
if (usb_dev->authorized == 0)
goto out_unauthorized;
@@ -2225,11 +2230,12 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
usb_dev->authorized = 0;
usb_set_configuration(usb_dev, -1);
- kfree(usb_dev->product);
+ product = usb_dev->product;
+ manufacturer = usb_dev->manufacturer;
+ serial = usb_dev->serial;
+
usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- kfree(usb_dev->manufacturer);
usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- kfree(usb_dev->serial);
usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
usb_destroy_configuration(usb_dev);
@@ -2237,6 +2243,12 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
out_unauthorized:
usb_unlock_device(usb_dev);
+ if (product || manufacturer || serial) {
+ synchronize_rcu();
+ kfree(product);
+ kfree(manufacturer);
+ kfree(serial);
+ }
return 0;
}
@@ -2244,6 +2256,9 @@ out_unauthorized:
int usb_authorize_device(struct usb_device *usb_dev)
{
int result = 0, c;
+ char *product = NULL;
+ char *manufacturer = NULL;
+ char *serial = NULL;
usb_lock_device(usb_dev);
if (usb_dev->authorized == 1)
@@ -2262,11 +2277,12 @@ int usb_authorize_device(struct usb_device *usb_dev)
goto error_device_descriptor;
}
- kfree(usb_dev->product);
+ product = usb_dev->product;
+ manufacturer = usb_dev->manufacturer;
+ serial = usb_dev->serial;
+
usb_dev->product = NULL;
- kfree(usb_dev->manufacturer);
usb_dev->manufacturer = NULL;
- kfree(usb_dev->serial);
usb_dev->serial = NULL;
usb_dev->authorized = 1;
@@ -2294,6 +2310,12 @@ error_device_descriptor:
error_autoresume:
out_authorized:
usb_unlock_device(usb_dev); // complements locktree
+ if (product || manufacturer || serial) {
+ synchronize_rcu();
+ kfree(product);
+ kfree(manufacturer);
+ kfree(serial);
+ }
return result;
}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 9a56e3adf47..45801b5678f 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -85,9 +85,9 @@ static ssize_t show_##name(struct device *dev, \
int retval; \
\
udev = to_usb_device(dev); \
- usb_lock_device(udev); \
- retval = sprintf(buf, "%s\n", udev->name); \
- usb_unlock_device(udev); \
+ rcu_read_lock(); \
+ retval = sprintf(buf, "%s\n", rcu_dereference(udev->name)); \
+ rcu_read_unlock(); \
return retval; \
} \
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);