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

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: 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 pointer
954 » » DBG("Invalid device\n"); 953 » * in the handle structure. */
954 » struct qcusbnet *ref = qcusbnet_get(dev);
955
956 » if (!ref)
955 return -ENXIO; 957 return -ENXIO;
956 }
957 958
958 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL); 959 file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
959 if (!file->private_data) { 960 if (!file->private_data) {
960 DBG("Mem error\n"); 961 DBG("Mem error\n");
961 return -ENOMEM; 962 return -ENOMEM;
962 } 963 }
963 964
964 handle = (struct qmihandle *)file->private_data; 965 handle = (struct qmihandle *)file->private_data;
965 handle->cid = (u16)-1; 966 handle->cid = (u16)-1;
966 » handle->dev = dev; 967 » handle->dev = ref;
967 968
968 DBG("%p %04x", handle, handle->cid); 969 DBG("%p %04x", handle, handle->cid);
969 970
970 return 0; 971 return 0;
971 } 972 }
972 973
973 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg) 974 static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd , unsigned long arg)
974 { 975 {
975 int result; 976 int result;
976 u32 vidpid; 977 u32 vidpid;
977 978
978 struct qmihandle *handle = (struct qmihandle *)file->private_data; 979 struct qmihandle *handle = (struct qmihandle *)file->private_data;
979 980
980 DBG("%p %04x %08x", handle, handle->cid, cmd); 981 DBG("%p %04x %08x", handle, handle->cid, cmd);
981 982
982 if (!handle) { 983 if (!handle) {
983 DBG("Bad file data\n"); 984 DBG("Bad file data\n");
984 return -EBADF; 985 return -EBADF;
985 } 986 }
986 987
988 if (handle->dev->dying) {
989 DBG("Dying device");
990 return -ENXIO;
991 }
992
987 if (!device_valid(handle->dev)) { 993 if (!device_valid(handle->dev)) {
988 DBG("Invalid device! Updating f_ops\n"); 994 DBG("Invalid device! Updating f_ops\n");
989 file->f_op = file->f_dentry->d_inode->i_fop; 995 file->f_op = file->f_dentry->d_inode->i_fop;
990 return -ENXIO; 996 return -ENXIO;
991 } 997 }
992 998
993 switch (cmd) { 999 switch (cmd) {
994 case IOCTL_QMI_GET_SERVICE_FILE: 1000 case IOCTL_QMI_GET_SERVICE_FILE:
995 1001
996 DBG("Setting up QMI for service %lu\n", arg); 1002 DBG("Setting up QMI for service %lu\n", arg);
997 if (!(u8)arg) { 1003 if (!(u8)arg) {
998 DBG("Cannot use QMICTL from userspace\n"); 1004 DBG("Cannot use QMICTL from userspace\n");
999 return -EINVAL; 1005 return -EINVAL;
1000 } 1006 }
1001 1007
1002 if (handle->cid != (u16)-1) { 1008 if (handle->cid != (u16)-1) {
1003 DBG("Close the current connection before opening a new o ne\n"); 1009 DBG("Close the current connection before opening a new o ne\n");
1004 return -EBADR; 1010 return -EBADR;
1005 } 1011 }
1006 1012
1007 result = client_alloc(handle->dev, (u8)arg); 1013 result = client_alloc(handle->dev, (u8)arg);
1008 if (result < 0) 1014 if (result < 0)
1009 return result; 1015 return result;
1010 handle->cid = result; 1016 handle->cid = result;
1011 1017
1012 return 0; 1018 return 0;
1013 break; 1019 break;
1014 1020
1015
1016 case IOCTL_QMI_GET_DEVICE_VIDPID: 1021 case IOCTL_QMI_GET_DEVICE_VIDPID:
1017 if (!arg) { 1022 if (!arg) {
1018 DBG("Bad VIDPID buffer\n"); 1023 DBG("Bad VIDPID buffer\n");
1019 return -EINVAL; 1024 return -EINVAL;
1020 } 1025 }
1021 1026
1022 if (!handle->dev->usbnet) { 1027 if (!handle->dev->usbnet) {
1023 DBG("Bad usbnet\n"); 1028 DBG("Bad usbnet\n");
1024 return -ENOMEM; 1029 return -ENOMEM;
1025 } 1030 }
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1120 int result; 1125 int result;
1121 void *data = NULL; 1126 void *data = NULL;
1122 void *smalldata; 1127 void *smalldata;
1123 struct qmihandle *handle = (struct qmihandle *)file->private_data; 1128 struct qmihandle *handle = (struct qmihandle *)file->private_data;
1124 1129
1125 if (!handle) { 1130 if (!handle) {
1126 DBG("Bad file data\n"); 1131 DBG("Bad file data\n");
1127 return -EBADF; 1132 return -EBADF;
1128 } 1133 }
1129 1134
1135 if (handle->dev->dying) {
1136 DBG("Dying device");
1137 return -ENXIO;
1138 }
1139
1130 if (!device_valid(handle->dev)) { 1140 if (!device_valid(handle->dev)) {
1131 DBG("Invalid device! Updating f_ops\n"); 1141 DBG("Invalid device! Updating f_ops\n");
1132 file->f_op = file->f_dentry->d_inode->i_fop; 1142 file->f_op = file->f_dentry->d_inode->i_fop;
1133 return -ENXIO; 1143 return -ENXIO;
1134 } 1144 }
1135 1145
1136 if (handle->cid == (u16)-1) { 1146 if (handle->cid == (u16)-1) {
1137 DBG("Client ID must be set before reading 0x%04X\n", 1147 DBG("Client ID must be set before reading 0x%04X\n",
1138 handle->cid); 1148 handle->cid);
1139 return -EBADR; 1149 return -EBADR;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1205 } 1215 }
1206 1216
1207 int qc_register(struct qcusbnet *dev) 1217 int qc_register(struct qcusbnet *dev)
1208 { 1218 {
1209 int result; 1219 int result;
1210 int qmiidx = 0; 1220 int qmiidx = 0;
1211 dev_t devno; 1221 dev_t devno;
1212 char *name; 1222 char *name;
1213 1223
1214 dev->valid = true; 1224 dev->valid = true;
1225 dev->dying = false;
1215 result = client_alloc(dev, QMICTL); 1226 result = client_alloc(dev, QMICTL);
1216 if (result) { 1227 if (result) {
1217 dev->valid = false; 1228 dev->valid = false;
1218 return result; 1229 return result;
1219 } 1230 }
1220 atomic_set(&dev->qmi.qmitid, 1); 1231 atomic_set(&dev->qmi.qmitid, 1);
1221 1232
1222 result = qc_startread(dev); 1233 result = qc_startread(dev);
1223 if (result) { 1234 if (result) {
1224 dev->valid = false; 1235 dev->valid = false;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1273 1284
1274 dev->qmi.devnum = devno; 1285 dev->qmi.devnum = devno;
1275 return 0; 1286 return 0;
1276 } 1287 }
1277 1288
1278 void qc_deregister(struct qcusbnet *dev) 1289 void qc_deregister(struct qcusbnet *dev)
1279 { 1290 {
1280 struct list_head *node, *tmp; 1291 struct list_head *node, *tmp;
1281 struct client *client; 1292 struct client *client;
1282 struct inode *inode; 1293 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