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>
This commit is contained in:
@@ -205,6 +205,7 @@ fw_device_op_read(struct file *file,
|
|||||||
return dequeue_event(client, buffer, count);
|
return dequeue_event(client, buffer, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* caller must hold card->lock so that node pointers can be dereferenced here */
|
||||||
static void
|
static void
|
||||||
fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
|
fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
|
||||||
struct client *client)
|
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->closure = client->bus_reset_closure;
|
||||||
event->type = FW_CDEV_EVENT_BUS_RESET;
|
event->type = FW_CDEV_EVENT_BUS_RESET;
|
||||||
event->generation = client->device->generation;
|
event->generation = client->device->generation;
|
||||||
smp_rmb(); /* node_id must not be older than generation */
|
|
||||||
event->node_id = client->device->node_id;
|
event->node_id = client->device->node_id;
|
||||||
event->local_node_id = card->local_node->node_id;
|
event->local_node_id = card->local_node->node_id;
|
||||||
event->bm_node_id = 0; /* FIXME: We don't track the BM. */
|
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_get_info *get_info = buffer;
|
||||||
struct fw_cdev_event_bus_reset bus_reset;
|
struct fw_cdev_event_bus_reset bus_reset;
|
||||||
|
struct fw_card *card = client->device->card;
|
||||||
unsigned long ret = 0;
|
unsigned long ret = 0;
|
||||||
|
|
||||||
client->version = get_info->version;
|
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;
|
client->bus_reset_closure = get_info->bus_reset_closure;
|
||||||
if (get_info->bus_reset != 0) {
|
if (get_info->bus_reset != 0) {
|
||||||
void __user *uptr = u64_to_uptr(get_info->bus_reset);
|
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);
|
fill_bus_reset_event(&bus_reset, client);
|
||||||
|
spin_unlock_irqrestore(&card->lock, flags);
|
||||||
|
|
||||||
if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
|
if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_info->card = client->device->card->index;
|
get_info->card = card->index;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user