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

Unified Diff: drivers/net/usb/gobi/qmidevice.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: Fix object-visibility race. 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
Index: drivers/net/usb/gobi/qmidevice.c
diff --git a/drivers/net/usb/gobi/qmidevice.c b/drivers/net/usb/gobi/qmidevice.c
index 82cb0fb3c1cad2227d14e146bf70c652639a459c..e7a87b5c5dfb9ab22d3685bbe8b83bc85ff2f140 100644
--- a/drivers/net/usb/gobi/qmidevice.c
+++ b/drivers/net/usb/gobi/qmidevice.c
@@ -736,7 +736,6 @@ static void client_free(struct qcusbnet *dev, u16 cid)
kfree(client);
}
}
-
spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
}
@@ -950,10 +949,13 @@ static int devqmi_open(struct inode *inode, struct file *file)
struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev);
struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi);
- if (!device_valid(dev)) {
- DBG("Invalid device\n");
+ /* We need an extra ref on the device per fd, since we stash a ref
+ * inside the handle. If qcusbnet_get() returns NULL, that means the
+ * device has been removed from the list - no new refs for us. */
+ struct qcusbnet *ref = qcusbnet_get(dev);
+
+ if (!ref)
return -ENXIO;
- }
file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
if (!file->private_data) {
@@ -963,7 +965,7 @@ static int devqmi_open(struct inode *inode, struct file *file)
handle = (struct qmihandle *)file->private_data;
handle->cid = (u16)-1;
- handle->dev = dev;
+ handle->dev = ref;
DBG("%p %04x", handle, handle->cid);
@@ -984,6 +986,11 @@ static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return -EBADF;
}
+ if (handle->dev->dying) {
Jason Glasgow 2011/03/10 18:36:28 Dying never set to true.
Elly Fong-Jones 2011/03/10 19:15:46 Oops. Supposed to be done early in qc_deregister()
+ DBG("Dying device");
+ return -ENXIO;
+ }
+
if (!device_valid(handle->dev)) {
DBG("Invalid device! Updating f_ops\n");
file->f_op = file->f_dentry->d_inode->i_fop;
@@ -1012,7 +1019,6 @@ static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return 0;
break;
-
case IOCTL_QMI_GET_DEVICE_VIDPID:
if (!arg) {
DBG("Bad VIDPID buffer\n");
@@ -1109,7 +1115,7 @@ static int devqmi_close(struct file *file, fl_owner_t ftable)
if (handle->cid != (u16)-1)
client_free(handle->dev, handle->cid);
-
+ qcusbnet_put(handle->dev);
kfree(handle);
return 0;
}
@@ -1127,6 +1133,11 @@ static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
return -EBADF;
}
+ if (handle->dev->dying) {
+ DBG("Dying device");
+ return -ENXIO;
+ }
+
if (!device_valid(handle->dev)) {
DBG("Invalid device! Updating f_ops\n");
file->f_op = file->f_dentry->d_inode->i_fop;
@@ -1212,6 +1223,7 @@ int qc_register(struct qcusbnet *dev)
char *name;
dev->valid = true;
+ dev->dying = false;
result = client_alloc(dev, QMICTL);
if (result) {
dev->valid = false;
@@ -1279,13 +1291,6 @@ void qc_deregister(struct qcusbnet *dev)
{
struct list_head *node, *tmp;
struct client *client;
- struct inode *inode;
- struct list_head *inodes;
- struct task_struct *task;
- struct fdtable *fdtable;
- struct file *file;
- unsigned long flags;
- int count = 0;
if (!device_valid(dev)) {
DBG("wrong device\n");
@@ -1300,33 +1305,6 @@ void qc_deregister(struct qcusbnet *dev)
qc_stopread(dev);
dev->valid = false;
- list_for_each(inodes, &dev->qmi.cdev.list) {
- inode = container_of(inodes, struct inode, i_devices);
- if (inode != NULL && !IS_ERR(inode)) {
- rcu_read_lock();
- for_each_process(task) {
- if (!task || !task->files)
- continue;
- spin_lock_irqsave(&task->files->file_lock, flags);
- fdtable = files_fdtable(task->files);
- for (count = 0; count < fdtable->max_fds; count++) {
- file = fdtable->fd[count];
- if (file != NULL && file->f_dentry != NULL) {
- if (file->f_dentry->d_inode == inode) {
- rcu_assign_pointer(fdtable->fd[count], NULL);
- spin_unlock_irqrestore(&task->files->file_lock, flags);
- DBG("forcing close of open file handle\n");
- filp_close(file, task->files);
- spin_lock_irqsave(&task->files->file_lock, flags);
- }
- }
- }
- spin_unlock_irqrestore(&task->files->file_lock, flags);
- }
- rcu_read_unlock();
- }
- }
-
if (!IS_ERR(dev->qmi.devclass))
device_destroy(dev->qmi.devclass, dev->qmi.devnum);
cdev_del(&dev->qmi.cdev);

Powered by Google App Engine
This is Rietveld 408576698