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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
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 718 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 urb = client_delurb(dev, cid); 729 urb = client_delurb(dev, cid);
730 } 730 }
731 731
732 while (client_delread(dev, cid, 0, &data, &size)) 732 while (client_delread(dev, cid, 0, &data, &size))
733 kfree(data); 733 kfree(data);
734 734
735 list_del(&client->node); 735 list_del(&client->node);
736 kfree(client); 736 kfree(client);
737 } 737 }
738 } 738 }
739
740 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags); 739 spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
741 } 740 }
742 741
743 struct client *client_bycid(struct qcusbnet *dev, u16 cid) 742 struct client *client_bycid(struct qcusbnet *dev, u16 cid)
744 { 743 {
745 struct list_head *node; 744 struct list_head *node;
746 struct client *client; 745 struct client *client;
747 746
748 if (!device_valid(dev)) { 747 if (!device_valid(dev)) {
749 DBG("Invalid device\n"); 748 DBG("Invalid device\n");
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
943 kfree(req); 942 kfree(req);
944 return urb; 943 return urb;
945 } 944 }
946 945
947 static int devqmi_open(struct inode *inode, struct file *file) 946 static int devqmi_open(struct inode *inode, struct file *file)
948 { 947 {
949 struct qmihandle *handle; 948 struct qmihandle *handle;
950 struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev) ; 949 struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev) ;
951 struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi); 950 struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi);
952 951
953 » if (!device_valid(dev)) { 952 » /* We need an extra ref on the device per fd, since we stash a ref
954 » » DBG("Invalid device\n"); 953 » * inside the handle. If qcusbnet_get() returns NULL, that means the
954 » * device has been removed from the list - no new refs for us. */
955 » struct qcusbnet *ref = qcusbnet_get(dev);
956
957 » if (!ref)
955 return -ENXIO; 958 return -ENXIO;
956 }
957 959
958 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL); 960 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
959 if (!file->private_data) { 961 if (!file->private_data) {
960 DBG("Mem error\n"); 962 DBG("Mem error\n");
961 return -ENOMEM; 963 return -ENOMEM;
962 } 964 }
963 965
964 handle = (struct qmihandle *)file->private_data; 966 handle = (struct qmihandle *)file->private_data;
965 handle->cid = (u16)-1; 967 handle->cid = (u16)-1;
966 » handle->dev = dev; 968 » handle->dev = ref;
967 969
968 DBG("%p %04x", handle, handle->cid); 970 DBG("%p %04x", handle, handle->cid);
969 971
970 return 0; 972 return 0;
971 } 973 }
972 974
973 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg) 975 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg)
974 { 976 {
975 int result; 977 int result;
976 u32 vidpid; 978 u32 vidpid;
977 979
978 struct qmihandle *handle = (struct qmihandle *)file->private_data; 980 struct qmihandle *handle = (struct qmihandle *)file->private_data;
979 981
980 DBG("%p %04x %08x", handle, handle->cid, cmd); 982 DBG("%p %04x %08x", handle, handle->cid, cmd);
981 983
982 if (!handle) { 984 if (!handle) {
983 DBG("Bad file data\n"); 985 DBG("Bad file data\n");
984 return -EBADF; 986 return -EBADF;
985 } 987 }
986 988
989 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()
990 DBG("Dying device");
991 return -ENXIO;
992 }
993
987 if (!device_valid(handle->dev)) { 994 if (!device_valid(handle->dev)) {
988 DBG("Invalid device! Updating f_ops\n"); 995 DBG("Invalid device! Updating f_ops\n");
989 file->f_op = file->f_dentry->d_inode->i_fop; 996 file->f_op = file->f_dentry->d_inode->i_fop;
990 return -ENXIO; 997 return -ENXIO;
991 } 998 }
992 999
993 switch (cmd) { 1000 switch (cmd) {
994 case IOCTL_QMI_GET_SERVICE_FILE: 1001 case IOCTL_QMI_GET_SERVICE_FILE:
995 1002
996 DBG("Setting up QMI for service %lu\n", arg); 1003 DBG("Setting up QMI for service %lu\n", arg);
997 if (!(u8)arg) { 1004 if (!(u8)arg) {
998 DBG("Cannot use QMICTL from userspace\n"); 1005 DBG("Cannot use QMICTL from userspace\n");
999 return -EINVAL; 1006 return -EINVAL;
1000 } 1007 }
1001 1008
1002 if (handle->cid != (u16)-1) { 1009 if (handle->cid != (u16)-1) {
1003 DBG("Close the current connection before opening a new o ne\n"); 1010 DBG("Close the current connection before opening a new o ne\n");
1004 return -EBADR; 1011 return -EBADR;
1005 } 1012 }
1006 1013
1007 result = client_alloc(handle->dev, (u8)arg); 1014 result = client_alloc(handle->dev, (u8)arg);
1008 if (result < 0) 1015 if (result < 0)
1009 return result; 1016 return result;
1010 handle->cid = result; 1017 handle->cid = result;
1011 1018
1012 return 0; 1019 return 0;
1013 break; 1020 break;
1014 1021
1015
1016 case IOCTL_QMI_GET_DEVICE_VIDPID: 1022 case IOCTL_QMI_GET_DEVICE_VIDPID:
1017 if (!arg) { 1023 if (!arg) {
1018 DBG("Bad VIDPID buffer\n"); 1024 DBG("Bad VIDPID buffer\n");
1019 return -EINVAL; 1025 return -EINVAL;
1020 } 1026 }
1021 1027
1022 if (!handle->dev->usbnet) { 1028 if (!handle->dev->usbnet) {
1023 DBG("Bad usbnet\n"); 1029 DBG("Bad usbnet\n");
1024 return -ENOMEM; 1030 return -ENOMEM;
1025 } 1031 }
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
1102 file->f_op = file->f_dentry->d_inode->i_fop; 1108 file->f_op = file->f_dentry->d_inode->i_fop;
1103 return -ENXIO; 1109 return -ENXIO;
1104 } 1110 }
1105 1111
1106 DBG("0x%04X\n", handle->cid); 1112 DBG("0x%04X\n", handle->cid);
1107 1113
1108 file->private_data = NULL; 1114 file->private_data = NULL;
1109 1115
1110 if (handle->cid != (u16)-1) 1116 if (handle->cid != (u16)-1)
1111 client_free(handle->dev, handle->cid); 1117 client_free(handle->dev, handle->cid);
1112 1118 » qcusbnet_put(handle->dev);
1113 kfree(handle); 1119 kfree(handle);
1114 return 0; 1120 return 0;
1115 } 1121 }
1116 1122
1117 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size, 1123 static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
1118 loff_t *pos) 1124 loff_t *pos)
1119 { 1125 {
1120 int result; 1126 int result;
1121 void *data = NULL; 1127 void *data = NULL;
1122 void *smalldata; 1128 void *smalldata;
1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; 1129 struct qmihandle *handle = (struct qmihandle *)file->private_data;
1124 1130
1125 if (!handle) { 1131 if (!handle) {
1126 DBG("Bad file data\n"); 1132 DBG("Bad file data\n");
1127 return -EBADF; 1133 return -EBADF;
1128 } 1134 }
1129 1135
1136 if (handle->dev->dying) {
1137 DBG("Dying device");
1138 return -ENXIO;
1139 }
1140
1130 if (!device_valid(handle->dev)) { 1141 if (!device_valid(handle->dev)) {
1131 DBG("Invalid device! Updating f_ops\n"); 1142 DBG("Invalid device! Updating f_ops\n");
1132 file->f_op = file->f_dentry->d_inode->i_fop; 1143 file->f_op = file->f_dentry->d_inode->i_fop;
1133 return -ENXIO; 1144 return -ENXIO;
1134 } 1145 }
1135 1146
1136 if (handle->cid == (u16)-1) { 1147 if (handle->cid == (u16)-1) {
1137 DBG("Client ID must be set before reading 0x%04X\n", 1148 DBG("Client ID must be set before reading 0x%04X\n",
1138 handle->cid); 1149 handle->cid);
1139 return -EBADR; 1150 return -EBADR;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1205 } 1216 }
1206 1217
1207 int qc_register(struct qcusbnet *dev) 1218 int qc_register(struct qcusbnet *dev)
1208 { 1219 {
1209 int result; 1220 int result;
1210 int qmiidx = 0; 1221 int qmiidx = 0;
1211 dev_t devno; 1222 dev_t devno;
1212 char *name; 1223 char *name;
1213 1224
1214 dev->valid = true; 1225 dev->valid = true;
1226 dev->dying = false;
1215 result = client_alloc(dev, QMICTL); 1227 result = client_alloc(dev, QMICTL);
1216 if (result) { 1228 if (result) {
1217 dev->valid = false; 1229 dev->valid = false;
1218 return result; 1230 return result;
1219 } 1231 }
1220 atomic_set(&dev->qmi.qmitid, 1); 1232 atomic_set(&dev->qmi.qmitid, 1);
1221 1233
1222 result = qc_startread(dev); 1234 result = qc_startread(dev);
1223 if (result) { 1235 if (result) {
1224 dev->valid = false; 1236 dev->valid = false;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1272 device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx); 1284 device_create(dev->qmi.devclass, NULL, devno, NULL, "qcqmi%d", qmiidx);
1273 1285
1274 dev->qmi.devnum = devno; 1286 dev->qmi.devnum = devno;
1275 return 0; 1287 return 0;
1276 } 1288 }
1277 1289
1278 void qc_deregister(struct qcusbnet *dev) 1290 void qc_deregister(struct qcusbnet *dev)
1279 { 1291 {
1280 struct list_head *node, *tmp; 1292 struct list_head *node, *tmp;
1281 struct client *client; 1293 struct client *client;
1282 struct inode *inode;
1283 struct list_head *inodes;
1284 struct task_struct *task;
1285 struct fdtable *fdtable;
1286 struct file *file;
1287 unsigned long flags;
1288 int count = 0;
1289 1294
1290 if (!device_valid(dev)) { 1295 if (!device_valid(dev)) {
1291 DBG("wrong device\n"); 1296 DBG("wrong device\n");
1292 return; 1297 return;
1293 } 1298 }
1294 1299
1295 list_for_each_safe(node, tmp, &dev->qmi.clients) { 1300 list_for_each_safe(node, tmp, &dev->qmi.clients) {
1296 client = list_entry(node, struct client, node); 1301 client = list_entry(node, struct client, node);
1297 DBG("release 0x%04X\n", client->cid); 1302 DBG("release 0x%04X\n", client->cid);
1298 client_free(dev, client->cid); 1303 client_free(dev, client->cid);
1299 } 1304 }
1300 1305
1301 qc_stopread(dev); 1306 qc_stopread(dev);
1302 dev->valid = false; 1307 dev->valid = false;
1303 list_for_each(inodes, &dev->qmi.cdev.list) {
1304 inode = container_of(inodes, struct inode, i_devices);
1305 if (inode != NULL && !IS_ERR(inode)) {
1306 rcu_read_lock();
1307 for_each_process(task) {
1308 if (!task || !task->files)
1309 continue;
1310 spin_lock_irqsave(&task->files->file_lock, flags );
1311 fdtable = files_fdtable(task->files);
1312 for (count = 0; count < fdtable->max_fds; count+ +) {
1313 file = fdtable->fd[count];
1314 if (file != NULL && file->f_dentry != N ULL) {
1315 if (file->f_dentry->d_inode == i node) {
1316 rcu_assign_pointer(fdtab le->fd[count], NULL);
1317 spin_unlock_irqrestore(& task->files->file_lock, flags);
1318 DBG("forcing close of op en file handle\n");
1319 filp_close(file, task->f iles);
1320 spin_lock_irqsave(&task- >files->file_lock, flags);
1321 }
1322 }
1323 }
1324 spin_unlock_irqrestore(&task->files->file_lock, flags);
1325 }
1326 rcu_read_unlock();
1327 }
1328 }
1329
1330 if (!IS_ERR(dev->qmi.devclass)) 1308 if (!IS_ERR(dev->qmi.devclass))
1331 device_destroy(dev->qmi.devclass, dev->qmi.devnum); 1309 device_destroy(dev->qmi.devclass, dev->qmi.devnum);
1332 cdev_del(&dev->qmi.cdev); 1310 cdev_del(&dev->qmi.cdev);
1333 unregister_chrdev_region(dev->qmi.devnum, 1); 1311 unregister_chrdev_region(dev->qmi.devnum, 1);
1334 } 1312 }
1335 1313
1336 static bool qmi_ready(struct qcusbnet *dev, u16 timeout) 1314 static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
1337 { 1315 {
1338 int result; 1316 int result;
1339 void *wbuf; 1317 void *wbuf;
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
1583 DBG("bad get MEID resp\n"); 1561 DBG("bad get MEID resp\n");
1584 memset(&dev->meid[0], '0', 14); 1562 memset(&dev->meid[0], '0', 14);
1585 } 1563 }
1586 1564
1587 client_free(dev, cid); 1565 client_free(dev, cid);
1588 return 0; 1566 return 0;
1589 } 1567 }
1590 1568
1591 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR); 1569 module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR);
1592 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware"); 1570 MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware");
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698