diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-06-06 22:11:30 +0200 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-06-19 00:12:35 +0200 |
commit | 5cb84067d646fa3889463129dad8b218806b4698 (patch) | |
tree | 081afef7668bf0e3e496fbefbe2d6256efb2d2dd /drivers/firewire | |
parent | firewire: fw-ohci: write selfIDBufferPtr before LinkControl.rcvSelfID (diff) | |
download | linux-5cb84067d646fa3889463129dad8b218806b4698.tar.xz linux-5cb84067d646fa3889463129dad8b218806b4698.zip |
firewire: fill_bus_reset_event needs lock protection
Callers of fill_bus_reset_event() have to take card->lock. Otherwise
access to node data may oops if node removal is in progress.
A lockless alternative would be
- event->local_node_id = card->local_node->node_id;
+ tmp = fw_node_get(card->local_node);
+ event->local_node_id = tmp->node_id;
+ fw_node_put(tmp);
and ditto with the other node pointers which fill_bus_reset_event()
accesses. But I went the locked route because one of the two callers
already holds the lock. As a bonus, we don't need the memory barrier
anymore because device->generation and device->node_id are written in
a card->lock protected section.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/fw-cdev.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index dda14015e873..c639915fc3cb 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c @@ -205,6 +205,7 @@ fw_device_op_read(struct file *file, 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) @@ -214,7 +215,6 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, event->closure = client->bus_reset_closure; event->type = FW_CDEV_EVENT_BUS_RESET; event->generation = client->device->generation; - smp_rmb(); /* node_id must not be older than generation */ event->node_id = client->device->node_id; event->local_node_id = card->local_node->node_id; event->bm_node_id = 0; /* FIXME: We don't track the BM. */ @@ -274,6 +274,7 @@ static int ioctl_get_info(struct client *client, void *buffer) { 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; @@ -299,13 +300,17 @@ static int ioctl_get_info(struct client *client, void *buffer) 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 = client->device->card->index; + get_info->card = card->index; return 0; } |