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>
This commit is contained in:
Родитель
1aa292bb1c
Коммит
cf417e5494
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
#include <linux/preempt.h>
|
#include <linux/preempt.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
|
@ -132,9 +133,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
file->private_data = client;
|
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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -205,12 +206,14 @@ 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)
|
||||||
{
|
{
|
||||||
struct fw_card *card = client->device->card;
|
struct fw_card *card = client->device->card;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&card->lock, flags);
|
||||||
|
|
||||||
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;
|
||||||
|
@ -220,22 +223,23 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
|
||||||
event->bm_node_id = 0; /* FIXME: We don't track the BM. */
|
event->bm_node_id = 0; /* FIXME: We don't track the BM. */
|
||||||
event->irm_node_id = card->irm_node->node_id;
|
event->irm_node_id = card->irm_node->node_id;
|
||||||
event->root_node_id = card->root_node->node_id;
|
event->root_node_id = card->root_node->node_id;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&card->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
for_each_client(struct fw_device *device,
|
for_each_client(struct fw_device *device,
|
||||||
void (*callback)(struct client *client))
|
void (*callback)(struct client *client))
|
||||||
{
|
{
|
||||||
struct fw_card *card = device->card;
|
|
||||||
struct client *c;
|
struct client *c;
|
||||||
unsigned long flags;
|
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)
|
list_for_each_entry(c, &device->client_list, link)
|
||||||
callback(c);
|
callback(c);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&card->lock, flags);
|
spin_unlock_irqrestore(&device->client_list_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -274,11 +278,11 @@ 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;
|
||||||
get_info->version = FW_CDEV_VERSION;
|
get_info->version = FW_CDEV_VERSION;
|
||||||
|
get_info->card = client->device->card->index;
|
||||||
|
|
||||||
down_read(&fw_device_rwsem);
|
down_read(&fw_device_rwsem);
|
||||||
|
|
||||||
|
@ -300,18 +304,12 @@ 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 = card->index;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,9 +1007,9 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
|
||||||
list_for_each_entry_safe(e, next_e, &client->event_list, link)
|
list_for_each_entry_safe(e, next_e, &client->event_list, link)
|
||||||
kfree(e);
|
kfree(e);
|
||||||
|
|
||||||
spin_lock_irqsave(&client->device->card->lock, flags);
|
spin_lock_irqsave(&client->device->client_list_lock, flags);
|
||||||
list_del(&client->link);
|
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);
|
fw_device_put(client->device);
|
||||||
kfree(client);
|
kfree(client);
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/semaphore.h>
|
#include <linux/semaphore.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include "fw-transaction.h"
|
#include "fw-transaction.h"
|
||||||
|
@ -1004,6 +1005,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
|
||||||
device->node = fw_node_get(node);
|
device->node = fw_node_get(node);
|
||||||
device->node_id = node->node_id;
|
device->node_id = node->node_id;
|
||||||
device->generation = card->generation;
|
device->generation = card->generation;
|
||||||
|
spin_lock_init(&device->client_list_lock);
|
||||||
INIT_LIST_HEAD(&device->client_list);
|
INIT_LIST_HEAD(&device->client_list);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/cdev.h>
|
#include <linux/cdev.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
enum fw_device_state {
|
enum fw_device_state {
|
||||||
|
@ -64,6 +65,8 @@ struct fw_device {
|
||||||
bool cmc;
|
bool cmc;
|
||||||
struct fw_card *card;
|
struct fw_card *card;
|
||||||
struct device device;
|
struct device device;
|
||||||
|
/* to prevent deadlocks, never take this lock with card->lock held */
|
||||||
|
spinlock_t client_list_lock;
|
||||||
struct list_head client_list;
|
struct list_head client_list;
|
||||||
u32 *config_rom;
|
u32 *config_rom;
|
||||||
size_t config_rom_length;
|
size_t config_rom_length;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче