diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-03-07 12:12:50 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:03:12 -0500 |
commit | 28cf6a04c82857d562968dc3a8a89726e6ac3dcb (patch) | |
tree | e4513c7e09c7f0bdb77a5358268c17a570e20fab /drivers | |
parent | f319b6a02f12c3712eb64eee6a23584367cb3588 (diff) |
firewire: Track pending transactions and cancel them on cdev release.
Without this, pending transactions will dereference freed memory
if they complete after the device file has been closed.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 23 |
1 files changed, 20 insertions, 3 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index 2d84284f7605..2946464fec62 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -60,6 +60,7 @@ struct response { | |||
60 | struct event event; | 60 | struct event event; |
61 | struct fw_transaction transaction; | 61 | struct fw_transaction transaction; |
62 | struct client *client; | 62 | struct client *client; |
63 | struct list_head link; | ||
63 | struct fw_cdev_event_response response; | 64 | struct fw_cdev_event_response response; |
64 | }; | 65 | }; |
65 | 66 | ||
@@ -74,6 +75,7 @@ struct client { | |||
74 | spinlock_t lock; | 75 | spinlock_t lock; |
75 | struct list_head handler_list; | 76 | struct list_head handler_list; |
76 | struct list_head request_list; | 77 | struct list_head request_list; |
78 | struct list_head transaction_list; | ||
77 | u32 request_serial; | 79 | u32 request_serial; |
78 | struct list_head event_list; | 80 | struct list_head event_list; |
79 | wait_queue_head_t wait; | 81 | wait_queue_head_t wait; |
@@ -115,6 +117,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
115 | INIT_LIST_HEAD(&client->event_list); | 117 | INIT_LIST_HEAD(&client->event_list); |
116 | INIT_LIST_HEAD(&client->handler_list); | 118 | INIT_LIST_HEAD(&client->handler_list); |
117 | INIT_LIST_HEAD(&client->request_list); | 119 | INIT_LIST_HEAD(&client->request_list); |
120 | INIT_LIST_HEAD(&client->transaction_list); | ||
118 | spin_lock_init(&client->lock); | 121 | spin_lock_init(&client->lock); |
119 | init_waitqueue_head(&client->wait); | 122 | init_waitqueue_head(&client->wait); |
120 | 123 | ||
@@ -299,6 +302,7 @@ complete_transaction(struct fw_card *card, int rcode, | |||
299 | { | 302 | { |
300 | struct response *response = data; | 303 | struct response *response = data; |
301 | struct client *client = response->client; | 304 | struct client *client = response->client; |
305 | unsigned long flags; | ||
302 | 306 | ||
303 | if (length < response->response.length) | 307 | if (length < response->response.length) |
304 | response->response.length = length; | 308 | response->response.length = length; |
@@ -306,6 +310,10 @@ complete_transaction(struct fw_card *card, int rcode, | |||
306 | memcpy(response->response.data, payload, | 310 | memcpy(response->response.data, payload, |
307 | response->response.length); | 311 | response->response.length); |
308 | 312 | ||
313 | spin_lock_irqsave(&client->lock, flags); | ||
314 | list_del(&response->link); | ||
315 | spin_unlock_irqrestore(&client->lock, flags); | ||
316 | |||
309 | response->response.type = FW_CDEV_EVENT_RESPONSE; | 317 | response->response.type = FW_CDEV_EVENT_RESPONSE; |
310 | response->response.rcode = rcode; | 318 | response->response.rcode = rcode; |
311 | queue_event(client, &response->event, | 319 | queue_event(client, &response->event, |
@@ -318,6 +326,7 @@ static ssize_t ioctl_send_request(struct client *client, void __user *arg) | |||
318 | struct fw_device *device = client->device; | 326 | struct fw_device *device = client->device; |
319 | struct fw_cdev_send_request request; | 327 | struct fw_cdev_send_request request; |
320 | struct response *response; | 328 | struct response *response; |
329 | unsigned long flags; | ||
321 | 330 | ||
322 | if (copy_from_user(&request, arg, sizeof request)) | 331 | if (copy_from_user(&request, arg, sizeof request)) |
323 | return -EFAULT; | 332 | return -EFAULT; |
@@ -341,6 +350,10 @@ static ssize_t ioctl_send_request(struct client *client, void __user *arg) | |||
341 | return -EFAULT; | 350 | return -EFAULT; |
342 | } | 351 | } |
343 | 352 | ||
353 | spin_lock_irqsave(&client->lock, flags); | ||
354 | list_add_tail(&response->link, &client->transaction_list); | ||
355 | spin_unlock_irqrestore(&client->lock, flags); | ||
356 | |||
344 | fw_send_request(device->card, &response->transaction, | 357 | fw_send_request(device->card, &response->transaction, |
345 | request.tcode & 0x1f, | 358 | request.tcode & 0x1f, |
346 | device->node->node_id, | 359 | device->node->node_id, |
@@ -752,6 +765,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
752 | struct address_handler *h, *next_h; | 765 | struct address_handler *h, *next_h; |
753 | struct request *r, *next_r; | 766 | struct request *r, *next_r; |
754 | struct event *e, *next_e; | 767 | struct event *e, *next_e; |
768 | struct response *t, *next_t; | ||
755 | unsigned long flags; | 769 | unsigned long flags; |
756 | 770 | ||
757 | if (client->buffer.pages) | 771 | if (client->buffer.pages) |
@@ -771,9 +785,12 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
771 | kfree(r); | 785 | kfree(r); |
772 | } | 786 | } |
773 | 787 | ||
774 | /* TODO: wait for all transactions to finish so | 788 | list_for_each_entry_safe(t, next_t, &client->transaction_list, link) |
775 | * complete_transaction doesn't try to queue up responses | 789 | fw_cancel_transaction(client->device->card, &t->transaction); |
776 | * after we free client. */ | 790 | |
791 | /* FIXME: We should wait for the async tasklets to stop | ||
792 | * running before freeing the memory. */ | ||
793 | |||
777 | list_for_each_entry_safe(e, next_e, &client->event_list, link) | 794 | list_for_each_entry_safe(e, next_e, &client->event_list, link) |
778 | kfree(e); | 795 | kfree(e); |
779 | 796 | ||