Commit a3aca3da authored by Kristian Høgsberg's avatar Kristian Høgsberg Committed by Stefan Richter

firewire: Switch cdev code over to use register_chrdev and keep a list of devices.

The old mechanism kept a struct cdev for each fw device, but fops->release
would reference this struct after the device got freed in some cases.
Signed-off-by: default avatarKristian Høgsberg <krh@redhat.com>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 344bbc4d
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/idr.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "fw-transaction.h" #include "fw-transaction.h"
...@@ -103,7 +104,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file) ...@@ -103,7 +104,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
struct client *client; struct client *client;
unsigned long flags; unsigned long flags;
device = container_of(inode->i_cdev, struct fw_device, cdev); device = fw_device_from_devt(inode->i_rdev);
if (device == NULL)
return -ENODEV;
client = kzalloc(sizeof *client, GFP_KERNEL); client = kzalloc(sizeof *client, GFP_KERNEL);
if (client == NULL) if (client == NULL)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/idr.h>
#include "fw-transaction.h" #include "fw-transaction.h"
#include "fw-topology.h" #include "fw-topology.h"
#include "fw-device.h" #include "fw-device.h"
...@@ -407,14 +408,31 @@ static int shutdown_unit(struct device *device, void *data) ...@@ -407,14 +408,31 @@ static int shutdown_unit(struct device *device, void *data)
return 0; return 0;
} }
static DEFINE_IDR(fw_device_idr);
int fw_cdev_major;
struct fw_device *fw_device_from_devt(dev_t devt)
{
struct fw_device *device;
down_read(&fw_bus_type.subsys.rwsem);
device = idr_find(&fw_device_idr, MINOR(devt));
up_read(&fw_bus_type.subsys.rwsem);
return device;
}
static void fw_device_shutdown(struct work_struct *work) static void fw_device_shutdown(struct work_struct *work)
{ {
struct fw_device *device = struct fw_device *device =
container_of(work, struct fw_device, work.work); container_of(work, struct fw_device, work.work);
int minor = MINOR(device->device.devt);
down_write(&fw_bus_type.subsys.rwsem);
idr_remove(&fw_device_idr, minor);
up_write(&fw_bus_type.subsys.rwsem);
device_remove_file(&device->device, &config_rom_attribute); device_remove_file(&device->device, &config_rom_attribute);
cdev_del(&device->cdev);
unregister_chrdev_region(device->device.devt, 1);
device_for_each_child(&device->device, NULL, shutdown_unit); device_for_each_child(&device->device, NULL, shutdown_unit);
device_unregister(&device->device); device_unregister(&device->device);
} }
...@@ -434,9 +452,9 @@ static void fw_device_shutdown(struct work_struct *work) ...@@ -434,9 +452,9 @@ static void fw_device_shutdown(struct work_struct *work)
static void fw_device_init(struct work_struct *work) static void fw_device_init(struct work_struct *work)
{ {
static atomic_t serial = ATOMIC_INIT(-1);
struct fw_device *device = struct fw_device *device =
container_of(work, struct fw_device, work.work); container_of(work, struct fw_device, work.work);
int minor, err;
/* All failure paths here set node->data to NULL, so that we /* All failure paths here set node->data to NULL, so that we
* don't try to do device_for_each_child() on a kfree()'d * don't try to do device_for_each_child() on a kfree()'d
...@@ -456,28 +474,24 @@ static void fw_device_init(struct work_struct *work) ...@@ -456,28 +474,24 @@ static void fw_device_init(struct work_struct *work)
return; return;
} }
err = -ENOMEM;
down_write(&fw_bus_type.subsys.rwsem);
if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
err = idr_get_new(&fw_device_idr, device, &minor);
up_write(&fw_bus_type.subsys.rwsem);
if (err < 0)
goto error;
device->device.bus = &fw_bus_type; device->device.bus = &fw_bus_type;
device->device.release = fw_device_release; device->device.release = fw_device_release;
device->device.parent = device->card->device; device->device.parent = device->card->device;
device->device.devt = MKDEV(fw_cdev_major, minor);
snprintf(device->device.bus_id, sizeof device->device.bus_id, snprintf(device->device.bus_id, sizeof device->device.bus_id,
"fw%d", atomic_inc_return(&serial)); "fw%d", minor);
if (alloc_chrdev_region(&device->device.devt, 0, 1, "fw")) {
fw_error("Failed to register char device region.\n");
goto error;
}
cdev_init(&device->cdev, &fw_device_ops);
device->cdev.owner = THIS_MODULE;
kobject_set_name(&device->cdev.kobj, device->device.bus_id);
if (cdev_add(&device->cdev, device->device.devt, 1)) {
fw_error("Failed to register char device.\n");
goto error;
}
if (device_add(&device->device)) { if (device_add(&device->device)) {
fw_error("Failed to add device.\n"); fw_error("Failed to add device.\n");
goto error; goto error_with_cdev;
} }
if (device_create_file(&device->device, &config_rom_attribute) < 0) { if (device_create_file(&device->device, &config_rom_attribute) < 0) {
...@@ -513,9 +527,11 @@ static void fw_device_init(struct work_struct *work) ...@@ -513,9 +527,11 @@ static void fw_device_init(struct work_struct *work)
error_with_device: error_with_device:
device_del(&device->device); device_del(&device->device);
error_with_cdev:
down_write(&fw_bus_type.subsys.rwsem);
idr_remove(&fw_device_idr, minor);
up_write(&fw_bus_type.subsys.rwsem);
error: error:
cdev_del(&device->cdev);
unregister_chrdev_region(device->device.devt, 1);
put_device(&device->device); put_device(&device->device);
} }
......
...@@ -39,7 +39,7 @@ struct fw_device { ...@@ -39,7 +39,7 @@ struct fw_device {
int generation; int generation;
struct fw_card *card; struct fw_card *card;
struct device device; struct device device;
struct cdev cdev; struct list_head link;
struct list_head client_list; struct list_head client_list;
__be32 *config_rom; __be32 *config_rom;
size_t config_rom_length; size_t config_rom_length;
...@@ -59,6 +59,9 @@ int fw_device_enable_phys_dma(struct fw_device *device); ...@@ -59,6 +59,9 @@ int fw_device_enable_phys_dma(struct fw_device *device);
void fw_device_cdev_update(struct fw_device *device); void fw_device_cdev_update(struct fw_device *device);
struct fw_device *fw_device_from_devt(dev_t devt);
extern int fw_cdev_major;
struct fw_unit { struct fw_unit {
struct device device; struct device device;
u32 *directory; u32 *directory;
......
...@@ -761,6 +761,12 @@ static int __init fw_core_init(void) ...@@ -761,6 +761,12 @@ static int __init fw_core_init(void)
if (retval < 0) if (retval < 0)
return retval; return retval;
fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
if (fw_cdev_major < 0) {
bus_unregister(&fw_bus_type);
return fw_cdev_major;
}
/* Add the vendor textual descriptor. */ /* Add the vendor textual descriptor. */
retval = fw_core_add_descriptor(&vendor_id_descriptor); retval = fw_core_add_descriptor(&vendor_id_descriptor);
BUG_ON(retval < 0); BUG_ON(retval < 0);
...@@ -772,6 +778,7 @@ static int __init fw_core_init(void) ...@@ -772,6 +778,7 @@ static int __init fw_core_init(void)
static void __exit fw_core_cleanup(void) static void __exit fw_core_cleanup(void)
{ {
unregister_chrdev(fw_cdev_major, "firewire");
bus_unregister(&fw_bus_type); bus_unregister(&fw_bus_type);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment