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

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: Remove bogus initializer. 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 0b931b8e8538ab129b1fca92b59cf660fc52d4ad..33e26a4540f4075028616b8a1df01cdb53c57816 100644
--- a/drivers/net/usb/gobi/qcusbnet.c
+++ b/drivers/net/usb/gobi/qcusbnet.c
@@ -25,9 +25,47 @@
#define DRIVER_AUTHOR "Qualcomm Innovation Center"
#define DRIVER_DESC "gobi"
+static LIST_HEAD(qcusbnet_list);
+static DEFINE_MUTEX(qcusbnet_lock);
+
int qcusbnet_debug;
static struct class *devclass;
+static void free_dev(struct kref *ref)
+{
+ struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount);
+ list_del(&dev->node);
+ kfree(dev);
+}
+
+void qcusbnet_put(struct qcusbnet *dev)
+{
+ mutex_lock(&qcusbnet_lock);
+ kref_put(&dev->refcount, free_dev);
+ mutex_unlock(&qcusbnet_lock);
+}
+
+struct qcusbnet *qcusbnet_get(struct qcusbnet *key)
+{
+ /* Given a putative qcusbnet struct, return either the struct itself
+ * (with a ref taken) if the struct is still visible, or NULL if it's
+ * not. This prevents object-visibility races where someone is looking
+ * up an object as the last ref gets dropped; dropping the last ref and
+ * removing the object from the list are atomic with respect to getting
+ * a new ref. */
+ struct qcusbnet *entry;
+ mutex_lock(&qcusbnet_lock);
+ list_for_each_entry(entry, &qcusbnet_list, node) {
+ if (entry == key) {
+ kref_get(&entry->refcount);
+ mutex_unlock(&qcusbnet_lock);
+ return entry;
+ }
+ }
+ mutex_unlock(&qcusbnet_lock);
+ return NULL;
+}
+
int qc_suspend(struct usb_interface *iface, pm_message_t event)
{
struct usbnet *usbnet;
@@ -185,8 +223,8 @@ static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
kfree(usbnet->net->netdev_ops);
usbnet->net->netdev_ops = NULL;
-
- kfree(dev);
+ /* drop the list's ref */
+ qcusbnet_put(dev);
}
static void qcnet_urbhook(struct urb *urb)
@@ -594,10 +632,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);
+ INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->qmi.clients);
init_completion(&dev->worker.work);
spin_lock_init(&dev->qmi.clients_lock);
@@ -609,6 +649,11 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
status = qc_register(dev);
if (status) {
qc_deregister(dev);
+ } else {
+ mutex_lock(&qcusbnet_lock);
+ /* Give our initial ref to the list */
+ list_add(&dev->node, &qcusbnet_list);
+ mutex_unlock(&qcusbnet_lock);
}
return status;
« 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