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

Side by Side Diff: drivers/net/usb/gobi/qmidevice.c

Issue 6694028: CHROMIUM: qcusbnet: fix devqmi_close() races. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/kernel.git@master
Patch Set: Update comment to reflect reality. 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* qmidevice.c - gobi QMI device 1 /* qmidevice.c - gobi QMI device
2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. 2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
3 3
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and 5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation. 6 * only version 2 as published by the Free Software Foundation.
7 7
8 * This program is distributed in the hope that it will be useful, 8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size); 63 static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size);
64 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid, 64 static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
65 void (*hook)(struct qcusbnet *, u16 cid, void *), 65 void (*hook)(struct qcusbnet *, u16 cid, void *),
66 void *data); 66 void *data);
67 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid); 67 static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid);
68 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb); 68 static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb);
69 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid); 69 static struct urb *client_delurb(struct qcusbnet *dev, u16 cid);
70 70
71 static int devqmi_open(struct inode *inode, struct file *file); 71 static int devqmi_open(struct inode *inode, struct file *file);
72 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg); 72 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg);
73 static int devqmi_close(struct file *file, fl_owner_t ftable); 73 static int devqmi_release(struct inode *inode, struct file *file);
74 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, 74 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
75 loff_t *pos); 75 loff_t *pos);
76 static ssize_t devqmi_write(struct file *file, const char __user *buf, 76 static ssize_t devqmi_write(struct file *file, const char __user *buf,
77 size_t size, loff_t *pos); 77 size_t size, loff_t *pos);
78 78
79 static bool qmi_ready(struct qcusbnet *dev, u16 timeout); 79 static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
80 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data); 80 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
81 static int setup_wds_callback(struct qcusbnet *dev); 81 static int setup_wds_callback(struct qcusbnet *dev);
82 static int qmidms_getmeid(struct qcusbnet *dev); 82 static int qmidms_getmeid(struct qcusbnet *dev);
83 83
84 #define IOCTL_QMI_GET_SERVICE_FILE» (0x8BE0 + 1) 84 #define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1)
85 #define IOCTL_QMI_GET_DEVICE_VIDPID» (0x8BE0 + 2) 85 #define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2)
86 #define IOCTL_QMI_GET_DEVICE_MEID» (0x8BE0 + 3) 86 #define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3)
87 #define IOCTL_QMI_CLOSE (0x8BE0 + 4)
87 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll 88 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
88 #define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll 89 #define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
89 90
90 static const struct file_operations devqmi_fops = { 91 static const struct file_operations devqmi_fops = {
91 » .owner = THIS_MODULE, 92 » .owner = THIS_MODULE,
92 » .read = devqmi_read, 93 » .read = devqmi_read,
93 » .write = devqmi_write, 94 » .write = devqmi_write,
94 » .ioctl = devqmi_ioctl, 95 » .ioctl = devqmi_ioctl,
95 » .open = devqmi_open, 96 » .open = devqmi_open,
96 » .flush = devqmi_close, 97 » .release = devqmi_release,
97 }; 98 };
98 99
99 #ifdef CONFIG_SMP 100 #ifdef CONFIG_SMP
100 static inline void assert_locked(struct qcusbnet *dev) 101 static inline void assert_locked(struct qcusbnet *dev)
101 { 102 {
102 BUG_ON(!spin_is_locked(&dev->qmi.clients_lock)); 103 BUG_ON(!spin_is_locked(&dev->qmi.clients_lock));
103 } 104 }
104 #else 105 #else
105 static inline void assert_locked(struct qcusbnet *dev) 106 static inline void assert_locked(struct qcusbnet *dev)
106 { 107 {
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after
672 struct urb *urb; 673 struct urb *urb;
673 void *data; 674 void *data;
674 u16 size; 675 u16 size;
675 void *wbuf; 676 void *wbuf;
676 size_t wbufsize; 677 size_t wbufsize;
677 void *rbuf; 678 void *rbuf;
678 u16 rbufsize; 679 u16 rbufsize;
679 unsigned long flags; 680 unsigned long flags;
680 u8 tid; 681 u8 tid;
681 682
682 if (!device_valid(dev)) {
683 DBG("invalid device\n");
684 return;
685 }
686
687 DBG("releasing 0x%04X\n", cid); 683 DBG("releasing 0x%04X\n", cid);
688 684
689 if (cid != QMICTL) { 685 if (cid != QMICTL) {
690 tid = atomic_add_return(1, &dev->qmi.qmitid); 686 tid = atomic_add_return(1, &dev->qmi.qmitid);
691 if (!tid) 687 if (!tid)
692 tid = atomic_add_return(1, &dev->qmi.qmitid); 688 tid = atomic_add_return(1, &dev->qmi.qmitid);
693 wbuf = qmictl_new_releasecid(tid, cid, &wbufsize); 689 wbuf = qmictl_new_releasecid(tid, cid, &wbufsize);
694 if (!wbuf) { 690 if (!wbuf) {
695 DBG("memory error\n"); 691 DBG("memory error\n");
696 } else { 692 } else {
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 struct qmihandle *handle = (struct qmihandle *)file->private_data; 974 struct qmihandle *handle = (struct qmihandle *)file->private_data;
979 975
980 DBG("%p %04x %08x", handle, handle->cid, cmd); 976 DBG("%p %04x %08x", handle, handle->cid, cmd);
981 977
982 if (!handle) { 978 if (!handle) {
983 DBG("Bad file data\n"); 979 DBG("Bad file data\n");
984 return -EBADF; 980 return -EBADF;
985 } 981 }
986 982
987 if (!device_valid(handle->dev)) { 983 if (!device_valid(handle->dev)) {
988 » » DBG("Invalid device! Updating f_ops\n"); 984 » » DBG("Invalid device!\n");
989 » » file->f_op = file->f_dentry->d_inode->i_fop;
990 return -ENXIO; 985 return -ENXIO;
991 } 986 }
992 987
993 switch (cmd) { 988 switch (cmd) {
994 case IOCTL_QMI_GET_SERVICE_FILE: 989 case IOCTL_QMI_GET_SERVICE_FILE:
995 990
996 DBG("Setting up QMI for service %lu\n", arg); 991 DBG("Setting up QMI for service %lu\n", arg);
997 if (!(u8)arg) { 992 if (!(u8)arg) {
998 DBG("Cannot use QMICTL from userspace\n"); 993 DBG("Cannot use QMICTL from userspace\n");
999 return -EINVAL; 994 return -EINVAL;
1000 } 995 }
1001 996
1002 if (handle->cid != (u16)-1) { 997 if (handle->cid != (u16)-1) {
1003 DBG("Close the current connection before opening a new o ne\n"); 998 DBG("Close the current connection before opening a new o ne\n");
1004 return -EBADR; 999 return -EBADR;
1005 } 1000 }
1006 1001
1007 result = client_alloc(handle->dev, (u8)arg); 1002 result = client_alloc(handle->dev, (u8)arg);
1008 if (result < 0) 1003 if (result < 0)
1009 return result; 1004 return result;
1010 handle->cid = result; 1005 handle->cid = result;
1011 1006
1012 return 0; 1007 return 0;
1013 break; 1008 break;
1014 1009
1010 /* Okay, all aboard the nasty hack express. If we don't have this
1011 * ioctl() (and we just rely on userspace to close() the file
1012 * descriptors), if userspace has any refs left to this fd (like, say, a
1013 * pending read()), then the read might hang around forever. Userspace
1014 * needs a way to cause us to kick people off those waitqueues before
1015 * closing the fd for good.
1016 *
1017 * If this driver used workqueues, the correct approach here would
1018 * instead be to make the file descriptor select()able, and then just
1019 * use select() instead of aio in userspace (thus allowing us to get
1020 * away with one thread total and avoiding the recounting mess
1021 * altogether).
1022 */
Mandeep Singh Baines 2011/03/15 20:56:31 I've read the comment but I'm still not getting th
1023 case IOCTL_QMI_CLOSE:
1024 DBG("Tearing down QMI for service %lu", arg);
1025 if (handle->cid == (u16)-1) {
1026 DBG("no qmi cid");
1027 return -EBADR;
1028 }
1029
1030 file->private_data = NULL;
1031 client_free(handle->dev, handle->cid);
1032 kfree(handle);
1033 return 0;
1034 break;
1015 1035
1016 case IOCTL_QMI_GET_DEVICE_VIDPID: 1036 case IOCTL_QMI_GET_DEVICE_VIDPID:
1017 if (!arg) { 1037 if (!arg) {
1018 DBG("Bad VIDPID buffer\n"); 1038 DBG("Bad VIDPID buffer\n");
1019 return -EINVAL; 1039 return -EINVAL;
1020 } 1040 }
1021 1041
1022 if (!handle->dev->usbnet) { 1042 if (!handle->dev->usbnet) {
1023 DBG("Bad usbnet\n"); 1043 DBG("Bad usbnet\n");
1024 return -ENOMEM; 1044 return -ENOMEM;
(...skipping 24 matching lines...) Expand all
1049 if (result) 1069 if (result)
1050 DBG("copy to userspace failure\n"); 1070 DBG("copy to userspace failure\n");
1051 1071
1052 return result; 1072 return result;
1053 break; 1073 break;
1054 default: 1074 default:
1055 return -EBADRQC; 1075 return -EBADRQC;
1056 } 1076 }
1057 } 1077 }
1058 1078
1059 static int devqmi_close(struct file *file, fl_owner_t ftable) 1079 static int devqmi_release(struct inode *inode, struct file *file)
1060 { 1080 {
1061 struct qmihandle *handle = (struct qmihandle *)file->private_data; 1081 struct qmihandle *handle = (struct qmihandle *)file->private_data;
1062 » struct task_struct *task; 1082 » if (!handle)
1063 » struct fdtable *fdtable; 1083 » » return 0;
1064 » int count = 0;
1065 » int used = 0;
1066 » unsigned long flags;
1067
1068 » if (!handle) {
1069 » » DBG("bad file data\n");
1070 » » return -EBADF;
1071 » }
1072
1073 » if (file_count(file) != 1) {
1074 » » rcu_read_lock();
1075 » » for_each_process(task) {
1076 » » » if (!task || !task->files)
1077 » » » » continue;
1078 » » » spin_lock_irqsave(&task->files->file_lock, flags);
1079 » » » fdtable = files_fdtable(task->files);
1080 » » » for (count = 0; count < fdtable->max_fds; count++) {
1081 » » » » /* Before this function was called, this file wa s removed
1082 » » » » * from our task's file table so if we find it i n a file
1083 » » » » * table then it is being used by another task
1084 » » » » */
1085 » » » » if (fdtable->fd[count] == file) {
1086 » » » » » used++;
1087 » » » » » break;
1088 » » » » }
1089 » » » }
1090 » » » spin_unlock_irqrestore(&task->files->file_lock, flags);
1091 » » }
1092 » » rcu_read_unlock();
1093
1094 » » if (used > 0) {
1095 » » » DBG("not closing, as this FD is open by %d other process \n", used);
1096 » » » return 0;
1097 » » }
1098 » }
1099
1100 » if (!device_valid(handle->dev)) {
1101 » » DBG("Invalid device! Updating f_ops\n");
1102 » » file->f_op = file->f_dentry->d_inode->i_fop;
1103 » » return -ENXIO;
1104 » }
1105
1106 » DBG("0x%04X\n", handle->cid);
1107
1108 file->private_data = NULL; 1084 file->private_data = NULL;
1109
1110 if (handle->cid != (u16)-1) 1085 if (handle->cid != (u16)-1)
1111 client_free(handle->dev, handle->cid); 1086 client_free(handle->dev, handle->cid);
1112
1113 kfree(handle); 1087 kfree(handle);
1114 return 0; 1088 return 0;
1115 } 1089 }
1116 1090
1117 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, 1091 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
1118 loff_t *pos) 1092 loff_t *pos)
1119 { 1093 {
1120 int result; 1094 int result;
1121 void *data = NULL; 1095 void *data = NULL;
1122 void *smalldata; 1096 void *smalldata;
1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; 1097 struct qmihandle *handle = (struct qmihandle *)file->private_data;
1124 1098
1125 if (!handle) { 1099 if (!handle) {
1126 DBG("Bad file data\n"); 1100 DBG("Bad file data\n");
1127 return -EBADF; 1101 return -EBADF;
1128 } 1102 }
1129 1103
1130 if (!device_valid(handle->dev)) { 1104 if (!device_valid(handle->dev)) {
1131 » » DBG("Invalid device! Updating f_ops\n"); 1105 » » DBG("Invalid device!\n");
1132 » » file->f_op = file->f_dentry->d_inode->i_fop;
1133 return -ENXIO; 1106 return -ENXIO;
1134 } 1107 }
1135 1108
1136 if (handle->cid == (u16)-1) { 1109 if (handle->cid == (u16)-1) {
1137 DBG("Client ID must be set before reading 0x%04X\n", 1110 DBG("Client ID must be set before reading 0x%04X\n",
1138 handle->cid); 1111 handle->cid);
1139 return -EBADR; 1112 return -EBADR;
1140 } 1113 }
1141 1114
1142 result = read_sync(handle->dev, &data, handle->cid, 0); 1115 result = read_sync(handle->dev, &data, handle->cid, 0);
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
1280 struct list_head *node, *tmp; 1253 struct list_head *node, *tmp;
1281 struct client *client; 1254 struct client *client;
1282 struct inode *inode; 1255 struct inode *inode;
1283 struct list_head *inodes; 1256 struct list_head *inodes;
1284 struct task_struct *task; 1257 struct task_struct *task;
1285 struct fdtable *fdtable; 1258 struct fdtable *fdtable;
1286 struct file *file; 1259 struct file *file;
1287 unsigned long flags; 1260 unsigned long flags;
1288 int count = 0; 1261 int count = 0;
1289 1262
1290 if (!device_valid(dev)) {
1291 DBG("wrong device\n");
1292 return;
1293 }
1294
1295 list_for_each_safe(node, tmp, &dev->qmi.clients) { 1263 list_for_each_safe(node, tmp, &dev->qmi.clients) {
1296 client = list_entry(node, struct client, node); 1264 client = list_entry(node, struct client, node);
1297 DBG("release 0x%04X\n", client->cid); 1265 DBG("release 0x%04X\n", client->cid);
1298 client_free(dev, client->cid); 1266 client_free(dev, client->cid);
1299 } 1267 }
1300 1268
1301 qc_stopread(dev); 1269 qc_stopread(dev);
1302 dev->valid = false; 1270 dev->valid = false;
1303 list_for_each(inodes, &dev->qmi.cdev.list) { 1271 list_for_each(inodes, &dev->qmi.cdev.list) {
1304 inode = container_of(inodes, struct inode, i_devices); 1272 inode = container_of(inodes, struct inode, i_devices);
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
1583 DBG("bad get MEID resp\n"); 1551 DBG("bad get MEID resp\n");
1584 memset(&dev->meid[0], '0', 14); 1552 memset(&dev->meid[0], '0', 14);
1585 } 1553 }
1586 1554
1587 client_free(dev, cid); 1555 client_free(dev, cid);
1588 return 0; 1556 return 0;
1589 } 1557 }
1590 1558
1591 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); 1559 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR);
1592 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); 1560 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware");
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698