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>
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c
index 2d84284..2946464 100644
--- a/drivers/firewire/fw-device-cdev.c
+++ b/drivers/firewire/fw-device-cdev.c
@@ -60,6 +60,7 @@
 	struct event event;
 	struct fw_transaction transaction;
 	struct client *client;
+	struct list_head link;
 	struct fw_cdev_event_response response;
 };
 
@@ -74,6 +75,7 @@
 	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 @@
 	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 @@
 {
 	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 @@
 		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 @@
 	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 @@
 		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 @@
 	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 @@
 		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);