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..74512ac4ca67c581e6146b8e03ae41634a9fc309 100644 |
--- a/drivers/net/usb/gobi/qmidevice.c |
+++ b/drivers/net/usb/gobi/qmidevice.c |
@@ -70,7 +70,7 @@ static struct urb *client_delurb(struct qcusbnet *dev, u16 cid); |
static int devqmi_open(struct inode *inode, struct file *file); |
static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); |
-static int devqmi_close(struct file *file, fl_owner_t ftable); |
+static int devqmi_release(struct inode *inode, struct file *file); |
static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
loff_t *pos); |
static ssize_t devqmi_write(struct file *file, const char __user *buf, |
@@ -81,19 +81,20 @@ static void wds_callback(struct qcusbnet *dev, u16 cid, void *data); |
static int setup_wds_callback(struct qcusbnet *dev); |
static int qmidms_getmeid(struct qcusbnet *dev); |
-#define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1) |
-#define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2) |
-#define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3) |
+#define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1) |
+#define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2) |
+#define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3) |
+#define IOCTL_QMI_CLOSE (0x8BE0 + 4) |
#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll |
#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll |
static const struct file_operations devqmi_fops = { |
- .owner = THIS_MODULE, |
- .read = devqmi_read, |
- .write = devqmi_write, |
- .ioctl = devqmi_ioctl, |
- .open = devqmi_open, |
- .flush = devqmi_close, |
+ .owner = THIS_MODULE, |
+ .read = devqmi_read, |
+ .write = devqmi_write, |
+ .ioctl = devqmi_ioctl, |
+ .open = devqmi_open, |
+ .release = devqmi_release, |
}; |
#ifdef CONFIG_SMP |
@@ -679,11 +680,6 @@ static void client_free(struct qcusbnet *dev, u16 cid) |
unsigned long flags; |
u8 tid; |
- if (!device_valid(dev)) { |
- DBG("invalid device\n"); |
- return; |
- } |
- |
DBG("releasing 0x%04X\n", cid); |
if (cid != QMICTL) { |
@@ -985,8 +981,7 @@ static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd |
} |
if (!device_valid(handle->dev)) { |
- DBG("Invalid device! Updating f_ops\n"); |
- file->f_op = file->f_dentry->d_inode->i_fop; |
+ DBG("Invalid device!\n"); |
return -ENXIO; |
} |
@@ -1012,6 +1007,31 @@ static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd |
return 0; |
break; |
+ /* Okay, all aboard the nasty hack express. If we don't have this |
+ * ioctl() (and we just rely on userspace to close() the file |
+ * descriptors), if userspace has any refs left to this fd (like, say, a |
+ * pending read()), then the read might hang around forever. Userspace |
+ * needs a way to cause us to kick people off those waitqueues before |
+ * closing the fd for good. |
+ * |
+ * If this driver used workqueues, the correct approach here would |
+ * instead be to make the file descriptor select()able, and then just |
+ * use select() instead of aio in userspace (thus allowing us to get |
+ * away with one thread total and avoiding the recounting mess |
+ * altogether). |
+ */ |
Mandeep Singh Baines
2011/03/15 20:56:31
I've read the comment but I'm still not getting th
|
+ case IOCTL_QMI_CLOSE: |
+ DBG("Tearing down QMI for service %lu", arg); |
+ if (handle->cid == (u16)-1) { |
+ DBG("no qmi cid"); |
+ return -EBADR; |
+ } |
+ |
+ file->private_data = NULL; |
+ client_free(handle->dev, handle->cid); |
+ kfree(handle); |
+ return 0; |
+ break; |
case IOCTL_QMI_GET_DEVICE_VIDPID: |
if (!arg) { |
@@ -1056,60 +1076,14 @@ static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd |
} |
} |
-static int devqmi_close(struct file *file, fl_owner_t ftable) |
+static int devqmi_release(struct inode *inode, struct file *file) |
{ |
struct qmihandle *handle = (struct qmihandle *)file->private_data; |
- struct task_struct *task; |
- struct fdtable *fdtable; |
- int count = 0; |
- int used = 0; |
- unsigned long flags; |
- |
- if (!handle) { |
- DBG("bad file data\n"); |
- return -EBADF; |
- } |
- |
- if (file_count(file) != 1) { |
- 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++) { |
- /* Before this function was called, this file was removed |
- * from our task's file table so if we find it in a file |
- * table then it is being used by another task |
- */ |
- if (fdtable->fd[count] == file) { |
- used++; |
- break; |
- } |
- } |
- spin_unlock_irqrestore(&task->files->file_lock, flags); |
- } |
- rcu_read_unlock(); |
- |
- if (used > 0) { |
- DBG("not closing, as this FD is open by %d other process\n", used); |
- return 0; |
- } |
- } |
- |
- if (!device_valid(handle->dev)) { |
- DBG("Invalid device! Updating f_ops\n"); |
- file->f_op = file->f_dentry->d_inode->i_fop; |
- return -ENXIO; |
- } |
- |
- DBG("0x%04X\n", handle->cid); |
- |
+ if (!handle) |
+ return 0; |
file->private_data = NULL; |
- |
if (handle->cid != (u16)-1) |
client_free(handle->dev, handle->cid); |
- |
kfree(handle); |
return 0; |
} |
@@ -1128,8 +1102,7 @@ static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, |
} |
if (!device_valid(handle->dev)) { |
- DBG("Invalid device! Updating f_ops\n"); |
- file->f_op = file->f_dentry->d_inode->i_fop; |
+ DBG("Invalid device!\n"); |
return -ENXIO; |
} |
@@ -1287,11 +1260,6 @@ void qc_deregister(struct qcusbnet *dev) |
unsigned long flags; |
int count = 0; |
- if (!device_valid(dev)) { |
- DBG("wrong device\n"); |
- return; |
- } |
- |
list_for_each_safe(node, tmp, &dev->qmi.clients) { |
client = list_entry(node, struct client, node); |
DBG("release 0x%04X\n", client->cid); |