aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/nosy.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-22 05:56:38 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-27 05:04:11 -0400
commit424d66cedae8bebb00fdb917fc8430f7b8a655cf (patch)
treecd232df29974be404978d47a2257c9422272d304 /drivers/firewire/nosy.c
parentb6d9c125e6610591c04ca9045f641e35ce1a9226 (diff)
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>
Diffstat (limited to 'drivers/firewire/nosy.c')
-rw-r--r--drivers/firewire/nosy.c111
1 files changed, 82 insertions, 29 deletions
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c
index ccf9c461bd86..edd729aafeca 100644
--- a/drivers/firewire/nosy.c
+++ b/drivers/firewire/nosy.c
@@ -23,8 +23,10 @@
23#include <linux/interrupt.h> 23#include <linux/interrupt.h>
24#include <linux/io.h> 24#include <linux/io.h>
25#include <linux/kernel.h> 25#include <linux/kernel.h>
26#include <linux/kref.h>
26#include <linux/miscdevice.h> 27#include <linux/miscdevice.h>
27#include <linux/module.h> 28#include <linux/module.h>
29#include <linux/mutex.h>
28#include <linux/pci.h> 30#include <linux/pci.h>
29#include <linux/poll.h> 31#include <linux/poll.h>
30#include <linux/sched.h> /* required for linux/wait.h */ 32#include <linux/sched.h> /* required for linux/wait.h */
@@ -104,8 +106,30 @@ struct pcilynx {
104 struct list_head client_list; 106 struct list_head client_list;
105 107
106 struct miscdevice misc; 108 struct miscdevice misc;
109 struct list_head link;
110 struct kref kref;
107}; 111};
108 112
113static inline struct pcilynx *
114lynx_get(struct pcilynx *lynx)
115{
116 kref_get(&lynx->kref);
117
118 return lynx;
119}
120
121static void
122lynx_release(struct kref *kref)
123{
124 kfree(container_of(kref, struct pcilynx, kref));
125}
126
127static inline void
128lynx_put(struct pcilynx *lynx)
129{
130 kref_put(&lynx->kref, lynx_release);
131}
132
109struct client { 133struct client {
110 struct pcilynx *lynx; 134 struct pcilynx *lynx;
111 u32 tcode_mask; 135 u32 tcode_mask;
@@ -113,8 +137,8 @@ struct client {
113 struct list_head link; 137 struct list_head link;
114}; 138};
115 139
116#define MAX_MINORS 64 140static DEFINE_MUTEX(card_mutex);
117static struct pcilynx *minors[MAX_MINORS]; 141static LIST_HEAD(card_list);
118 142
119static int 143static int
120packet_buffer_init(struct packet_buffer *buffer, size_t capacity) 144packet_buffer_init(struct packet_buffer *buffer, size_t capacity)
@@ -139,15 +163,20 @@ packet_buffer_destroy(struct packet_buffer *buffer)
139} 163}
140 164
141static int 165static int
142packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) 166packet_buffer_get(struct client *client, void *data, size_t user_length)
143{ 167{
168 struct packet_buffer *buffer = &client->buffer;
144 size_t length; 169 size_t length;
145 char *end; 170 char *end;
146 171
147 if (wait_event_interruptible(buffer->wait, 172 if (wait_event_interruptible(buffer->wait,
148 atomic_read(&buffer->size) > 0)) 173 atomic_read(&buffer->size) > 0) ||
174 list_empty(&client->lynx->link))
149 return -ERESTARTSYS; 175 return -ERESTARTSYS;
150 176
177 if (atomic_read(&buffer->size) == 0)
178 return -ENODEV;
179
151 /* FIXME: Check length <= user_length. */ 180 /* FIXME: Check length <= user_length. */
152 181
153 end = buffer->data + buffer->capacity; 182 end = buffer->data + buffer->capacity;
@@ -265,39 +294,52 @@ nosy_open(struct inode *inode, struct file *file)
265{ 294{
266 int minor = iminor(inode); 295 int minor = iminor(inode);
267 struct client *client; 296 struct client *client;
268 297 struct pcilynx *tmp, *lynx = NULL;
269 if (minor > MAX_MINORS || minors[minor] == NULL) 298
299 mutex_lock(&card_mutex);
300 list_for_each_entry(tmp, &card_list, link)
301 if (tmp->misc.minor == minor) {
302 lynx = lynx_get(tmp);
303 break;
304 }
305 mutex_unlock(&card_mutex);
306 if (lynx == NULL)
270 return -ENODEV; 307 return -ENODEV;
271 308
272 client = kmalloc(sizeof *client, GFP_KERNEL); 309 client = kmalloc(sizeof *client, GFP_KERNEL);
273 if (client == NULL) 310 if (client == NULL)
274 return -ENOMEM; 311 goto fail;
275 312
276 client->tcode_mask = ~0; 313 client->tcode_mask = ~0;
277 client->lynx = minors[minor]; 314 client->lynx = lynx;
278 INIT_LIST_HEAD(&client->link); 315 INIT_LIST_HEAD(&client->link);
279 316
280 if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { 317 if (packet_buffer_init(&client->buffer, 128 * 1024) < 0)
281 kfree(client); 318 goto fail;
282 return -ENOMEM;
283 }
284 319
285 file->private_data = client; 320 file->private_data = client;
286 321
287 return 0; 322 return 0;
323fail:
324 kfree(client);
325 lynx_put(lynx);
326
327 return -ENOMEM;
288} 328}
289 329
290static int 330static int
291nosy_release(struct inode *inode, struct file *file) 331nosy_release(struct inode *inode, struct file *file)
292{ 332{
293 struct client *client = file->private_data; 333 struct client *client = file->private_data;
334 struct pcilynx *lynx = client->lynx;
294 335
295 spin_lock_irq(&client->lynx->client_list_lock); 336 spin_lock_irq(&lynx->client_list_lock);
296 list_del_init(&client->link); 337 list_del_init(&client->link);
297 spin_unlock_irq(&client->lynx->client_list_lock); 338 spin_unlock_irq(&lynx->client_list_lock);
298 339
299 packet_buffer_destroy(&client->buffer); 340 packet_buffer_destroy(&client->buffer);
300 kfree(client); 341 kfree(client);
342 lynx_put(lynx);
301 343
302 return 0; 344 return 0;
303} 345}
@@ -306,13 +348,17 @@ static unsigned int
306nosy_poll(struct file *file, poll_table *pt) 348nosy_poll(struct file *file, poll_table *pt)
307{ 349{
308 struct client *client = file->private_data; 350 struct client *client = file->private_data;
351 unsigned int ret = 0;
309 352
310 poll_wait(file, &client->buffer.wait, pt); 353 poll_wait(file, &client->buffer.wait, pt);
311 354
312 if (atomic_read(&client->buffer.size) > 0) 355 if (atomic_read(&client->buffer.size) > 0)
313 return POLLIN | POLLRDNORM; 356 ret = POLLIN | POLLRDNORM;
314 else 357
315 return 0; 358 if (list_empty(&client->lynx->link))
359 ret |= POLLHUP;
360
361 return ret;
316} 362}
317 363
318static ssize_t 364static ssize_t
@@ -320,7 +366,7 @@ nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset)
320{ 366{
321 struct client *client = file->private_data; 367 struct client *client = file->private_data;
322 368
323 return packet_buffer_get(&client->buffer, buffer, count); 369 return packet_buffer_get(client, buffer, count);
324} 370}
325 371
326static long 372static long
@@ -479,16 +525,22 @@ irq_handler(int irq, void *device)
479static void 525static void
480remove_card(struct pci_dev *dev) 526remove_card(struct pci_dev *dev)
481{ 527{
482 struct pcilynx *lynx; 528 struct pcilynx *lynx = pci_get_drvdata(dev);
529 struct client *client;
483 530
484 lynx = pci_get_drvdata(dev); 531 mutex_lock(&card_mutex);
485 if (!lynx) 532 list_del_init(&lynx->link);
486 return; 533 misc_deregister(&lynx->misc);
487 pci_set_drvdata(dev, NULL); 534 mutex_unlock(&card_mutex);
488 535
489 reg_write(lynx, PCI_INT_ENABLE, 0); 536 reg_write(lynx, PCI_INT_ENABLE, 0);
490 free_irq(lynx->pci_device->irq, lynx); 537 free_irq(lynx->pci_device->irq, lynx);
491 538
539 spin_lock_irq(&lynx->client_list_lock);
540 list_for_each_entry(client, &lynx->client_list, link)
541 wake_up_interruptible(&client->buffer.wait);
542 spin_unlock_irq(&lynx->client_list_lock);
543
492 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 544 pci_free_consistent(lynx->pci_device, sizeof(struct pcl),
493 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); 545 lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus);
494 pci_free_consistent(lynx->pci_device, sizeof(struct pcl), 546 pci_free_consistent(lynx->pci_device, sizeof(struct pcl),
@@ -498,11 +550,7 @@ remove_card(struct pci_dev *dev)
498 550
499 iounmap(lynx->registers); 551 iounmap(lynx->registers);
500 pci_disable_device(dev); 552 pci_disable_device(dev);
501 553 lynx_put(lynx);
502 minors[lynx->misc.minor] = NULL;
503 misc_deregister(&lynx->misc);
504
505 kfree(lynx);
506} 554}
507 555
508#define RCV_BUFFER_SIZE (16 * 1024) 556#define RCV_BUFFER_SIZE (16 * 1024)
@@ -536,6 +584,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
536 584
537 spin_lock_init(&lynx->client_list_lock); 585 spin_lock_init(&lynx->client_list_lock);
538 INIT_LIST_HEAD(&lynx->client_list); 586 INIT_LIST_HEAD(&lynx->client_list);
587 kref_init(&lynx->kref);
539 588
540 lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), 589 lynx->registers = ioremap_nocache(pci_resource_start(dev, 0),
541 PCILYNX_MAX_REGISTER); 590 PCILYNX_MAX_REGISTER);
@@ -619,12 +668,16 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
619 lynx->misc.minor = MISC_DYNAMIC_MINOR; 668 lynx->misc.minor = MISC_DYNAMIC_MINOR;
620 lynx->misc.name = "nosy"; 669 lynx->misc.name = "nosy";
621 lynx->misc.fops = &nosy_ops; 670 lynx->misc.fops = &nosy_ops;
671
672 mutex_lock(&card_mutex);
622 ret = misc_register(&lynx->misc); 673 ret = misc_register(&lynx->misc);
623 if (ret) { 674 if (ret) {
624 error("Failed to register misc char device\n"); 675 error("Failed to register misc char device\n");
676 mutex_unlock(&card_mutex);
625 goto fail_free_irq; 677 goto fail_free_irq;
626 } 678 }
627 minors[lynx->misc.minor] = lynx; 679 list_add_tail(&lynx->link, &card_list);
680 mutex_unlock(&card_mutex);
628 681
629 notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); 682 notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq);
630 683