firewire: add a client_list_lock

This adds a client_list_lock, which only protects the device's
client_list, so that future versions of the driver can call code that
takes the card->lock while holding the client_list_lock.  Adding this
lock is much simpler than adding __ versions of all the functions that
the future version may need.  The one ordering issue is to make sure
code never takes the client_list_lock with card->lock held.  Since
client_list_lock is only used in three places, that isn't hard.

Signed-off-by: Jay Fenlason <fenlason@redhat.com>

Update fill_bus_reset_event() accordingly.  Include linux/spinlock.h.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index ed03234..40cc973 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -27,6 +27,7 @@
 #include <linux/poll.h>
 #include <linux/preempt.h>
 #include <linux/time.h>
+#include <linux/spinlock.h>
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/idr.h>
@@ -132,9 +133,9 @@
 
 	file->private_data = client;
 
-	spin_lock_irqsave(&device->card->lock, flags);
+	spin_lock_irqsave(&device->client_list_lock, flags);
 	list_add_tail(&client->link, &device->client_list);
-	spin_unlock_irqrestore(&device->card->lock, flags);
+	spin_unlock_irqrestore(&device->client_list_lock, flags);
 
 	return 0;
 }
@@ -205,12 +206,14 @@
 	return dequeue_event(client, buffer, count);
 }
 
-/* caller must hold card->lock so that node pointers can be dereferenced here */
 static void
 fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
 		     struct client *client)
 {
 	struct fw_card *card = client->device->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
 
 	event->closure	     = client->bus_reset_closure;
 	event->type          = FW_CDEV_EVENT_BUS_RESET;
@@ -220,22 +223,23 @@
 	event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
 	event->irm_node_id   = card->irm_node->node_id;
 	event->root_node_id  = card->root_node->node_id;
+
+	spin_unlock_irqrestore(&card->lock, flags);
 }
 
 static void
 for_each_client(struct fw_device *device,
 		void (*callback)(struct client *client))
 {
-	struct fw_card *card = device->card;
 	struct client *c;
 	unsigned long flags;
 
-	spin_lock_irqsave(&card->lock, flags);
+	spin_lock_irqsave(&device->client_list_lock, flags);
 
 	list_for_each_entry(c, &device->client_list, link)
 		callback(c);
 
-	spin_unlock_irqrestore(&card->lock, flags);
+	spin_unlock_irqrestore(&device->client_list_lock, flags);
 }
 
 static void
@@ -274,11 +278,11 @@
 {
 	struct fw_cdev_get_info *get_info = buffer;
 	struct fw_cdev_event_bus_reset bus_reset;
-	struct fw_card *card = client->device->card;
 	unsigned long ret = 0;
 
 	client->version = get_info->version;
 	get_info->version = FW_CDEV_VERSION;
+	get_info->card = client->device->card->index;
 
 	down_read(&fw_device_rwsem);
 
@@ -300,18 +304,12 @@
 	client->bus_reset_closure = get_info->bus_reset_closure;
 	if (get_info->bus_reset != 0) {
 		void __user *uptr = u64_to_uptr(get_info->bus_reset);
-		unsigned long flags;
 
-		spin_lock_irqsave(&card->lock, flags);
 		fill_bus_reset_event(&bus_reset, client);
-		spin_unlock_irqrestore(&card->lock, flags);
-
 		if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
 			return -EFAULT;
 	}
 
-	get_info->card = card->index;
-
 	return 0;
 }
 
@@ -1009,9 +1007,9 @@
 	list_for_each_entry_safe(e, next_e, &client->event_list, link)
 		kfree(e);
 
-	spin_lock_irqsave(&client->device->card->lock, flags);
+	spin_lock_irqsave(&client->device->client_list_lock, flags);
 	list_del(&client->link);
-	spin_unlock_irqrestore(&client->device->card->lock, flags);
+	spin_unlock_irqrestore(&client->device->client_list_lock, flags);
 
 	fw_device_put(client->device);
 	kfree(client);