From ae884f341c47a26482e77aa8cf1ef20a38432d0c Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Thu, 1 Nov 2012 16:52:46 -0400 Subject: [PATCH] CHROMIUM: gobi: Keep struct qcusbnet alive while cdev is alive This fixes an issue where struct qcusbnet is freed while the embedded character device is still alive. This uses the ability of cdev to pin an arbitrary kobject by setting cdev.kobj.parent. We have to switch from kref to kobject to use that. This is arguably misuse of kobject, but char_dev requires it.t t BUG=chrome-os-partner:15849 TEST=suspend_stress_test Change-Id: I309e80c6ec91e491d30ea0bfcb062ae7e55f7198 Signed-off-by: Michael Spang Reviewed-on: https://gerrit.chromium.org/gerrit/37135 Reviewed-by: Vincent Palatin --- drivers/net/usb/gobi/qcusbnet.c | 15 ++++++++++----- drivers/net/usb/gobi/qmidevice.c | 1 + drivers/net/usb/gobi/structs.h | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/gobi/qcusbnet.c b/drivers/net/usb/gobi/qcusbnet.c index cf4e4adc32c56..36f1c2325a767 100644 --- a/drivers/net/usb/gobi/qcusbnet.c +++ b/drivers/net/usb/gobi/qcusbnet.c @@ -34,9 +34,9 @@ static DEFINE_MUTEX(qcusbnet_lock); int gobi_debug; static struct class *devclass; -static void free_dev(struct kref *ref) +static void free_dev(struct kobject *ref) { - struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount); + struct qcusbnet *dev = container_of(ref, struct qcusbnet, kobj); list_del(&dev->node); kfree(dev); } @@ -48,10 +48,14 @@ static void free_urb_with_skb(struct urb *urb) usb_free_urb(urb); } +static struct kobj_type ktype_qcusbnet = { + .release = free_dev, +}; + void qcusbnet_put(struct qcusbnet *dev) { mutex_lock(&qcusbnet_lock); - kref_put(&dev->refcount, free_dev); + kobject_put(&dev->kobj); mutex_unlock(&qcusbnet_lock); } @@ -67,7 +71,7 @@ struct qcusbnet *qcusbnet_get(struct qcusbnet *key) mutex_lock(&qcusbnet_lock); list_for_each_entry(entry, &qcusbnet_list, node) { if (entry == key) { - kref_get(&entry->refcount); + kobject_get(&entry->kobj); mutex_unlock(&qcusbnet_lock); return entry; } @@ -650,7 +654,8 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids dev->qmi.devclass = devclass; - kref_init(&dev->refcount); + memset(&dev->kobj, 0, sizeof(dev->kobj)); + kobject_init(&dev->kobj, &ktype_qcusbnet); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->qmi.clients); dev->workqueue = alloc_ordered_workqueue("gobi", 0); diff --git a/drivers/net/usb/gobi/qmidevice.c b/drivers/net/usb/gobi/qmidevice.c index b7075535a2f94..d7bf4288ae4ed 100644 --- a/drivers/net/usb/gobi/qmidevice.c +++ b/drivers/net/usb/gobi/qmidevice.c @@ -1548,6 +1548,7 @@ int qc_register(struct qcusbnet *dev) cdev_init(&dev->qmi.cdev, &devqmi_fops); dev->qmi.cdev.owner = THIS_MODULE; dev->qmi.cdev.ops = &devqmi_fops; + dev->qmi.cdev.kobj.parent = &dev->kobj; result = cdev_add(&dev->qmi.cdev, devno, 1); if (result) { diff --git a/drivers/net/usb/gobi/structs.h b/drivers/net/usb/gobi/structs.h index bf215266adbf1..e256d6800a0f7 100644 --- a/drivers/net/usb/gobi/structs.h +++ b/drivers/net/usb/gobi/structs.h @@ -80,7 +80,7 @@ enum { struct qcusbnet { struct list_head node; - struct kref refcount; + struct kobject kobj; struct usbnet *usbnet; struct usb_interface *iface; int (*open)(struct net_device *); -- 2.39.5