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; |