Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firewire: nosy: fix device shutdown with active client

Fix race between nosy_open() and remove_card() by replacing the
unprotected array of card pointers by a mutex-protected list of cards.

Make card instances reference-counted and let each client hold a
reference.

Notify clients about card removal via POLLHUP in poll()'s events
bitmap; also let read() fail with errno=ENODEV if the card was removed
and everything in the buffer was read.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>

+81 -28
+81 -28
drivers/firewire/nosy.c
··· 23 23 #include <linux/interrupt.h> 24 24 #include <linux/io.h> 25 25 #include <linux/kernel.h> 26 + #include <linux/kref.h> 26 27 #include <linux/miscdevice.h> 27 28 #include <linux/module.h> 29 + #include <linux/mutex.h> 28 30 #include <linux/pci.h> 29 31 #include <linux/poll.h> 30 32 #include <linux/sched.h> /* required for linux/wait.h */ ··· 106 104 struct list_head client_list; 107 105 108 106 struct miscdevice misc; 107 + struct list_head link; 108 + struct kref kref; 109 109 }; 110 + 111 + static inline struct pcilynx * 112 + lynx_get(struct pcilynx *lynx) 113 + { 114 + kref_get(&lynx->kref); 115 + 116 + return lynx; 117 + } 118 + 119 + static void 120 + lynx_release(struct kref *kref) 121 + { 122 + kfree(container_of(kref, struct pcilynx, kref)); 123 + } 124 + 125 + static inline void 126 + lynx_put(struct pcilynx *lynx) 127 + { 128 + kref_put(&lynx->kref, lynx_release); 129 + } 110 130 111 131 struct client { 112 132 struct pcilynx *lynx; ··· 137 113 struct list_head link; 138 114 }; 139 115 140 - #define MAX_MINORS 64 141 - static struct pcilynx *minors[MAX_MINORS]; 116 + static DEFINE_MUTEX(card_mutex); 117 + static LIST_HEAD(card_list); 142 118 143 119 static int 144 120 packet_buffer_init(struct packet_buffer *buffer, size_t capacity) ··· 163 139 } 164 140 165 141 static int 166 - packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) 142 + packet_buffer_get(struct client *client, void *data, size_t user_length) 167 143 { 144 + struct packet_buffer *buffer = &client->buffer; 168 145 size_t length; 169 146 char *end; 170 147 171 148 if (wait_event_interruptible(buffer->wait, 172 - atomic_read(&buffer->size) > 0)) 149 + atomic_read(&buffer->size) > 0) || 150 + list_empty(&client->lynx->link)) 173 151 return -ERESTARTSYS; 152 + 153 + if (atomic_read(&buffer->size) == 0) 154 + return -ENODEV; 174 155 175 156 /* FIXME: Check length <= user_length. */ 176 157 ··· 294 265 { 295 266 int minor = iminor(inode); 296 267 struct client *client; 268 + struct pcilynx *tmp, *lynx = NULL; 297 269 298 - if (minor > MAX_MINORS || minors[minor] == NULL) 270 + mutex_lock(&card_mutex); 271 + list_for_each_entry(tmp, &card_list, link) 272 + if (tmp->misc.minor == minor) { 273 + lynx = lynx_get(tmp); 274 + break; 275 + } 276 + mutex_unlock(&card_mutex); 277 + if (lynx == NULL) 299 278 return -ENODEV; 300 279 301 280 client = kmalloc(sizeof *client, GFP_KERNEL); 302 281 if (client == NULL) 303 - return -ENOMEM; 282 + goto fail; 304 283 305 284 client->tcode_mask = ~0; 306 - client->lynx = minors[minor]; 285 + client->lynx = lynx; 307 286 INIT_LIST_HEAD(&client->link); 308 287 309 - if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { 310 - kfree(client); 311 - return -ENOMEM; 312 - } 288 + if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) 289 + goto fail; 313 290 314 291 file->private_data = client; 315 292 316 293 return 0; 294 + fail: 295 + kfree(client); 296 + lynx_put(lynx); 297 + 298 + return -ENOMEM; 317 299 } 318 300 319 301 static int 320 302 nosy_release(struct inode *inode, struct file *file) 321 303 { 322 304 struct client *client = file->private_data; 305 + struct pcilynx *lynx = client->lynx; 323 306 324 - spin_lock_irq(&client->lynx->client_list_lock); 307 + spin_lock_irq(&lynx->client_list_lock); 325 308 list_del_init(&client->link); 326 - spin_unlock_irq(&client->lynx->client_list_lock); 309 + spin_unlock_irq(&lynx->client_list_lock); 327 310 328 311 packet_buffer_destroy(&client->buffer); 329 312 kfree(client); 313 + lynx_put(lynx); 330 314 331 315 return 0; 332 316 } ··· 348 306 nosy_poll(struct file *file, poll_table *pt) 349 307 { 350 308 struct client *client = file->private_data; 309 + unsigned int ret = 0; 351 310 352 311 poll_wait(file, &client->buffer.wait, pt); 353 312 354 313 if (atomic_read(&client->buffer.size) > 0) 355 - return POLLIN | POLLRDNORM; 356 - else 357 - return 0; 314 + ret = POLLIN | POLLRDNORM; 315 + 316 + if (list_empty(&client->lynx->link)) 317 + ret |= POLLHUP; 318 + 319 + return ret; 358 320 } 359 321 360 322 static ssize_t ··· 366 320 { 367 321 struct client *client = file->private_data; 368 322 369 - return packet_buffer_get(&client->buffer, buffer, count); 323 + return packet_buffer_get(client, buffer, count); 370 324 } 371 325 372 326 static long ··· 525 479 static void 526 480 remove_card(struct pci_dev *dev) 527 481 { 528 - struct pcilynx *lynx; 482 + struct pcilynx *lynx = pci_get_drvdata(dev); 483 + struct client *client; 529 484 530 - lynx = pci_get_drvdata(dev); 531 - if (!lynx) 532 - return; 533 - pci_set_drvdata(dev, NULL); 485 + mutex_lock(&card_mutex); 486 + list_del_init(&lynx->link); 487 + misc_deregister(&lynx->misc); 488 + mutex_unlock(&card_mutex); 534 489 535 490 reg_write(lynx, PCI_INT_ENABLE, 0); 536 491 free_irq(lynx->pci_device->irq, lynx); 492 + 493 + spin_lock_irq(&lynx->client_list_lock); 494 + list_for_each_entry(client, &lynx->client_list, link) 495 + wake_up_interruptible(&client->buffer.wait); 496 + spin_unlock_irq(&lynx->client_list_lock); 537 497 538 498 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 539 499 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); ··· 550 498 551 499 iounmap(lynx->registers); 552 500 pci_disable_device(dev); 553 - 554 - minors[lynx->misc.minor] = NULL; 555 - misc_deregister(&lynx->misc); 556 - 557 - kfree(lynx); 501 + lynx_put(lynx); 558 502 } 559 503 560 504 #define RCV_BUFFER_SIZE (16 * 1024) ··· 584 536 585 537 spin_lock_init(&lynx->client_list_lock); 586 538 INIT_LIST_HEAD(&lynx->client_list); 539 + kref_init(&lynx->kref); 587 540 588 541 lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), 589 542 PCILYNX_MAX_REGISTER); ··· 668 619 lynx->misc.minor = MISC_DYNAMIC_MINOR; 669 620 lynx->misc.name = "nosy"; 670 621 lynx->misc.fops = &nosy_ops; 622 + 623 + mutex_lock(&card_mutex); 671 624 ret = misc_register(&lynx->misc); 672 625 if (ret) { 673 626 error("Failed to register misc char device\n"); 627 + mutex_unlock(&card_mutex); 674 628 goto fail_free_irq; 675 629 } 676 - minors[lynx->misc.minor] = lynx; 630 + list_add_tail(&lynx->link, &card_list); 631 + mutex_unlock(&card_mutex); 677 632 678 633 notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); 679 634