diff options
author | Jay Fenlason <fenlason@redhat.com> | 2008-10-03 11:19:09 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-03-24 15:56:36 -0400 |
commit | cf417e5494582453c033d8cac9e1352e74215435 (patch) | |
tree | a1681ef863c8c4219506e16b7982e51dc0718387 /drivers | |
parent | 1aa292bb1c53500e3ab570b955d03afa97a9404d (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/fw-cdev.c | 28 | ||||
-rw-r--r-- | drivers/firewire/fw-device.c | 2 | ||||
-rw-r--r-- | drivers/firewire/fw-device.h | 3 |
3 files changed, 18 insertions, 15 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index ed03234cbea8..40cc9732dc28 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/poll.h> | 27 | #include <linux/poll.h> |
28 | #include <linux/preempt.h> | 28 | #include <linux/preempt.h> |
29 | #include <linux/time.h> | 29 | #include <linux/time.h> |
30 | #include <linux/spinlock.h> | ||
30 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
31 | #include <linux/mm.h> | 32 | #include <linux/mm.h> |
32 | #include <linux/idr.h> | 33 | #include <linux/idr.h> |
@@ -132,9 +133,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
132 | 133 | ||
133 | file->private_data = client; | 134 | file->private_data = client; |
134 | 135 | ||
135 | spin_lock_irqsave(&device->card->lock, flags); | 136 | spin_lock_irqsave(&device->client_list_lock, flags); |
136 | list_add_tail(&client->link, &device->client_list); | 137 | list_add_tail(&client->link, &device->client_list); |
137 | spin_unlock_irqrestore(&device->card->lock, flags); | 138 | spin_unlock_irqrestore(&device->client_list_lock, flags); |
138 | 139 | ||
139 | return 0; | 140 | return 0; |
140 | } | 141 | } |
@@ -205,12 +206,14 @@ fw_device_op_read(struct file *file, | |||
205 | return dequeue_event(client, buffer, count); | 206 | return dequeue_event(client, buffer, count); |
206 | } | 207 | } |
207 | 208 | ||
208 | /* caller must hold card->lock so that node pointers can be dereferenced here */ | ||
209 | static void | 209 | static void |
210 | fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, | 210 | fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, |
211 | struct client *client) | 211 | struct client *client) |
212 | { | 212 | { |
213 | struct fw_card *card = client->device->card; | 213 | struct fw_card *card = client->device->card; |
214 | unsigned long flags; | ||
215 | |||
216 | spin_lock_irqsave(&card->lock, flags); | ||
214 | 217 | ||
215 | event->closure = client->bus_reset_closure; | 218 | event->closure = client->bus_reset_closure; |
216 | event->type = FW_CDEV_EVENT_BUS_RESET; | 219 | event->type = FW_CDEV_EVENT_BUS_RESET; |
@@ -220,22 +223,23 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, | |||
220 | event->bm_node_id = 0; /* FIXME: We don't track the BM. */ | 223 | event->bm_node_id = 0; /* FIXME: We don't track the BM. */ |
221 | event->irm_node_id = card->irm_node->node_id; | 224 | event->irm_node_id = card->irm_node->node_id; |
222 | event->root_node_id = card->root_node->node_id; | 225 | event->root_node_id = card->root_node->node_id; |
226 | |||
227 | spin_unlock_irqrestore(&card->lock, flags); | ||
223 | } | 228 | } |
224 | 229 | ||
225 | static void | 230 | static void |
226 | for_each_client(struct fw_device *device, | 231 | for_each_client(struct fw_device *device, |
227 | void (*callback)(struct client *client)) | 232 | void (*callback)(struct client *client)) |
228 | { | 233 | { |
229 | struct fw_card *card = device->card; | ||
230 | struct client *c; | 234 | struct client *c; |
231 | unsigned long flags; | 235 | unsigned long flags; |
232 | 236 | ||
233 | spin_lock_irqsave(&card->lock, flags); | 237 | spin_lock_irqsave(&device->client_list_lock, flags); |
234 | 238 | ||
235 | list_for_each_entry(c, &device->client_list, link) | 239 | list_for_each_entry(c, &device->client_list, link) |
236 | callback(c); | 240 | callback(c); |
237 | 241 | ||
238 | spin_unlock_irqrestore(&card->lock, flags); | 242 | spin_unlock_irqrestore(&device->client_list_lock, flags); |
239 | } | 243 | } |
240 | 244 | ||
241 | static void | 245 | static void |
@@ -274,11 +278,11 @@ static int ioctl_get_info(struct client *client, void *buffer) | |||
274 | { | 278 | { |
275 | struct fw_cdev_get_info *get_info = buffer; | 279 | struct fw_cdev_get_info *get_info = buffer; |
276 | struct fw_cdev_event_bus_reset bus_reset; | 280 | struct fw_cdev_event_bus_reset bus_reset; |
277 | struct fw_card *card = client->device->card; | ||
278 | unsigned long ret = 0; | 281 | unsigned long ret = 0; |
279 | 282 | ||
280 | client->version = get_info->version; | 283 | client->version = get_info->version; |
281 | get_info->version = FW_CDEV_VERSION; | 284 | get_info->version = FW_CDEV_VERSION; |
285 | get_info->card = client->device->card->index; | ||
282 | 286 | ||
283 | down_read(&fw_device_rwsem); | 287 | down_read(&fw_device_rwsem); |
284 | 288 | ||
@@ -300,18 +304,12 @@ static int ioctl_get_info(struct client *client, void *buffer) | |||
300 | client->bus_reset_closure = get_info->bus_reset_closure; | 304 | client->bus_reset_closure = get_info->bus_reset_closure; |
301 | if (get_info->bus_reset != 0) { | 305 | if (get_info->bus_reset != 0) { |
302 | void __user *uptr = u64_to_uptr(get_info->bus_reset); | 306 | void __user *uptr = u64_to_uptr(get_info->bus_reset); |
303 | unsigned long flags; | ||
304 | 307 | ||
305 | spin_lock_irqsave(&card->lock, flags); | ||
306 | fill_bus_reset_event(&bus_reset, client); | 308 | fill_bus_reset_event(&bus_reset, client); |
307 | spin_unlock_irqrestore(&card->lock, flags); | ||
308 | |||
309 | if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) | 309 | if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) |
310 | return -EFAULT; | 310 | return -EFAULT; |
311 | } | 311 | } |
312 | 312 | ||
313 | get_info->card = card->index; | ||
314 | |||
315 | return 0; | 313 | return 0; |
316 | } | 314 | } |
317 | 315 | ||
@@ -1009,9 +1007,9 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
1009 | list_for_each_entry_safe(e, next_e, &client->event_list, link) | 1007 | list_for_each_entry_safe(e, next_e, &client->event_list, link) |
1010 | kfree(e); | 1008 | kfree(e); |
1011 | 1009 | ||
1012 | spin_lock_irqsave(&client->device->card->lock, flags); | 1010 | spin_lock_irqsave(&client->device->client_list_lock, flags); |
1013 | list_del(&client->link); | 1011 | list_del(&client->link); |
1014 | spin_unlock_irqrestore(&client->device->card->lock, flags); | 1012 | spin_unlock_irqrestore(&client->device->client_list_lock, flags); |
1015 | 1013 | ||
1016 | fw_device_put(client->device); | 1014 | fw_device_put(client->device); |
1017 | kfree(client); | 1015 | kfree(client); |
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index bf53acb45652..ffde1bed46b2 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/string.h> | 29 | #include <linux/string.h> |
30 | #include <linux/rwsem.h> | 30 | #include <linux/rwsem.h> |
31 | #include <linux/semaphore.h> | 31 | #include <linux/semaphore.h> |
32 | #include <linux/spinlock.h> | ||
32 | #include <asm/system.h> | 33 | #include <asm/system.h> |
33 | #include <linux/ctype.h> | 34 | #include <linux/ctype.h> |
34 | #include "fw-transaction.h" | 35 | #include "fw-transaction.h" |
@@ -1004,6 +1005,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
1004 | device->node = fw_node_get(node); | 1005 | device->node = fw_node_get(node); |
1005 | device->node_id = node->node_id; | 1006 | device->node_id = node->node_id; |
1006 | device->generation = card->generation; | 1007 | device->generation = card->generation; |
1008 | spin_lock_init(&device->client_list_lock); | ||
1007 | INIT_LIST_HEAD(&device->client_list); | 1009 | INIT_LIST_HEAD(&device->client_list); |
1008 | 1010 | ||
1009 | /* | 1011 | /* |
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index 8ef6ec2ca21c..008a7908a865 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/cdev.h> | 23 | #include <linux/cdev.h> |
24 | #include <linux/idr.h> | 24 | #include <linux/idr.h> |
25 | #include <linux/rwsem.h> | 25 | #include <linux/rwsem.h> |
26 | #include <linux/spinlock.h> | ||
26 | #include <asm/atomic.h> | 27 | #include <asm/atomic.h> |
27 | 28 | ||
28 | enum fw_device_state { | 29 | enum fw_device_state { |
@@ -64,6 +65,8 @@ struct fw_device { | |||
64 | bool cmc; | 65 | bool cmc; |
65 | struct fw_card *card; | 66 | struct fw_card *card; |
66 | struct device device; | 67 | struct device device; |
68 | /* to prevent deadlocks, never take this lock with card->lock held */ | ||
69 | spinlock_t client_list_lock; | ||
67 | struct list_head client_list; | 70 | struct list_head client_list; |
68 | u32 *config_rom; | 71 | u32 *config_rom; |
69 | size_t config_rom_length; | 72 | size_t config_rom_length; |