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