Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1701)

Unified Diff: drivers/net/usb/gobi/qcusbnet.c

Issue 6612045: CHROMIUM: gobi: Fix races in qc_deregister() once and for all. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/kernel.git@master
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « drivers/net/usb/gobi/qcusbnet.h ('k') | drivers/net/usb/gobi/qmidevice.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: drivers/net/usb/gobi/qcusbnet.c
diff --git a/drivers/net/usb/gobi/qcusbnet.c b/drivers/net/usb/gobi/qcusbnet.c
index 7e218a81fe3217e7a7595ceabd2aaabc251b3da6..47ef8808e5022cb2c6134f2aa49435e01bfcdafe 100644
--- a/drivers/net/usb/gobi/qcusbnet.c
+++ b/drivers/net/usb/gobi/qcusbnet.c
@@ -28,6 +28,32 @@
int qcusbnet_debug;
static struct class *devclass;
+static void free_dev(struct kref *ref)
+{
+ struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount);
+ kfree(dev);
+}
+
+void qcusbnet_put(struct qcusbnet *dev)
+{
+ kref_put(&dev->refcount, free_dev);
+}
+
+struct qcusbnet *qcusbnet_get(struct qcusbnet *dev)
+{
+ /* If we don't take dev->lock here, we can interleave with qc_deregister
+ * such that they set dying and drop their ref between our test and our
+ * kref_get(). Boo. :( */
+ mutex_lock(&dev->lock);
Jason Glasgow 2011/03/09 23:47:55 What if the other thread "wins" the race and sets
Elly Fong-Jones 2011/03/10 18:29:13 There is something to what you say. I will meditat
+ if (dev->dying || !dev->valid) {
+ mutex_unlock(&dev->lock);
+ return NULL;
+ }
+ kref_get(&dev->refcount);
+ mutex_unlock(&dev->lock);
+ return dev;
+}
+
int qc_suspend(struct usb_interface *iface, pm_message_t event)
{
struct usbnet *usbnet;
@@ -180,12 +206,15 @@ static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0];
netif_carrier_off(usbnet->net);
+ /* See the comments in qcusbnet_get() for an explanation of why we need
+ * this lock. */
+ mutex_lock(&dev->lock);
qc_deregister(dev);
+ mutex_unlock(&dev->lock);
- kfree(usbnet->net->netdev_ops);
usbnet->net->netdev_ops = NULL;
-
- kfree(dev);
+ kfree(usbnet->net->netdev_ops);
Jason Glasgow 2011/03/09 23:47:55 Like Olof said, kfree(NULL)?
Elly Fong-Jones 2011/03/10 18:29:13 Argh.
+ qcusbnet_put(dev);
}
static void qcnet_urbhook(struct urb *urb)
@@ -590,10 +619,12 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr);
dev->valid = false;
- memset(&dev->qmi, 0, sizeof(struct qmidev));
+ memset(&dev->qmi, 0, sizeof(dev->qmi));
dev->qmi.devclass = devclass;
+ kref_init(&dev->refcount);
+ mutex_init(&dev->lock);
INIT_LIST_HEAD(&dev->qmi.clients);
init_completion(&dev->worker.work);
spin_lock_init(&dev->qmi.clients_lock);
« no previous file with comments | « drivers/net/usb/gobi/qcusbnet.h ('k') | drivers/net/usb/gobi/qmidevice.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698