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 22:03:12 +0100 |
commit | 28cf6a04c82857d562968dc3a8a89726e6ac3dcb (patch) | |
tree | e4513c7e09c7f0bdb77a5358268c17a570e20fab /drivers/firewire | |
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/firewire')
-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 { struct event event; struct fw_transaction transaction; struct client *client; + struct list_head link; struct fw_cdev_event_response response; }; @@ -74,6 +75,7 @@ struct client { spinlock_t lock; struct list_head handler_list; struct list_head request_list; + struct list_head transaction_list; u32 request_serial; struct list_head event_list; wait_queue_head_t wait; @@ -115,6 +117,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&client->event_list); INIT_LIST_HEAD(&client->handler_list); INIT_LIST_HEAD(&client->request_list); + INIT_LIST_HEAD(&client->transaction_list); spin_lock_init(&client->lock); init_waitqueue_head(&client->wait); @@ -299,6 +302,7 @@ complete_transaction(struct fw_card *card, int rcode, { struct response *response = data; struct client *client = response->client; + unsigned long flags; if (length < response->response.length) response->response.length = length; @@ -306,6 +310,10 @@ complete_transaction(struct fw_card *card, int rcode, memcpy(response->response.data, payload, response->response.length); + spin_lock_irqsave(&client->lock, flags); + list_del(&response->link); + spin_unlock_irqrestore(&client->lock, flags); + response->response.type = FW_CDEV_EVENT_RESPONSE; response->response.rcode = rcode; queue_event(client, &response->event, @@ -318,6 +326,7 @@ static ssize_t ioctl_send_request(struct client *client, void __user *arg) struct fw_device *device = client->device; struct fw_cdev_send_request request; struct response *response; + unsigned long flags; if (copy_from_user(&request, arg, sizeof request)) return -EFAULT; @@ -341,6 +350,10 @@ static ssize_t ioctl_send_request(struct client *client, void __user *arg) return -EFAULT; } + spin_lock_irqsave(&client->lock, flags); + list_add_tail(&response->link, &client->transaction_list); + spin_unlock_irqrestore(&client->lock, flags); + fw_send_request(device->card, &response->transaction, request.tcode & 0x1f, device->node->node_id, @@ -752,6 +765,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) struct address_handler *h, *next_h; struct request *r, *next_r; struct event *e, *next_e; + struct response *t, *next_t; unsigned long flags; if (client->buffer.pages) @@ -771,9 +785,12 @@ static int fw_device_op_release(struct inode *inode, struct file *file) kfree(r); } - /* TODO: wait for all transactions to finish so - * complete_transaction doesn't try to queue up responses - * after we free client. */ + list_for_each_entry_safe(t, next_t, &client->transaction_list, link) + fw_cancel_transaction(client->device->card, &t->transaction); + + /* FIXME: We should wait for the async tasklets to stop + * running before freeing the memory. */ + list_for_each_entry_safe(e, next_e, &client->event_list, link) kfree(e); |