diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-03-07 12:12:44 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:03:09 -0500 |
commit | a3aca3dabbcf00f2088d472f27755c29acaa992e (patch) | |
tree | c6b23c1d51a7eab5bee4137d344264974ddfc432 | |
parent | 344bbc4de14e70d03f09bff04bb7d161b8a0d28c (diff) |
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: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 5 | ||||
-rw-r--r-- | drivers/firewire/fw-device.c | 56 | ||||
-rw-r--r-- | drivers/firewire/fw-device.h | 5 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.c | 7 |
4 files changed, 51 insertions, 22 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index d9f3bb2be1c2..54ef27b2adb5 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/poll.h> | 28 | #include <linux/poll.h> |
29 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
30 | #include <linux/mm.h> | 30 | #include <linux/mm.h> |
31 | #include <linux/idr.h> | ||
31 | #include <linux/compat.h> | 32 | #include <linux/compat.h> |
32 | #include <asm/uaccess.h> | 33 | #include <asm/uaccess.h> |
33 | #include "fw-transaction.h" | 34 | #include "fw-transaction.h" |
@@ -103,7 +104,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
103 | struct client *client; | 104 | struct client *client; |
104 | unsigned long flags; | 105 | unsigned long flags; |
105 | 106 | ||
106 | device = container_of(inode->i_cdev, struct fw_device, cdev); | 107 | device = fw_device_from_devt(inode->i_rdev); |
108 | if (device == NULL) | ||
109 | return -ENODEV; | ||
107 | 110 | ||
108 | client = kzalloc(sizeof *client, GFP_KERNEL); | 111 | client = kzalloc(sizeof *client, GFP_KERNEL); |
109 | if (client == NULL) | 112 | if (client == NULL) |
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index ccc05e511a4f..b24090ae9c70 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kthread.h> | 25 | #include <linux/kthread.h> |
26 | #include <linux/device.h> | 26 | #include <linux/device.h> |
27 | #include <linux/delay.h> | 27 | #include <linux/delay.h> |
28 | #include <linux/idr.h> | ||
28 | #include "fw-transaction.h" | 29 | #include "fw-transaction.h" |
29 | #include "fw-topology.h" | 30 | #include "fw-topology.h" |
30 | #include "fw-device.h" | 31 | #include "fw-device.h" |
@@ -407,14 +408,31 @@ static int shutdown_unit(struct device *device, void *data) | |||
407 | return 0; | 408 | return 0; |
408 | } | 409 | } |
409 | 410 | ||
411 | static DEFINE_IDR(fw_device_idr); | ||
412 | int fw_cdev_major; | ||
413 | |||
414 | struct fw_device *fw_device_from_devt(dev_t devt) | ||
415 | { | ||
416 | struct fw_device *device; | ||
417 | |||
418 | down_read(&fw_bus_type.subsys.rwsem); | ||
419 | device = idr_find(&fw_device_idr, MINOR(devt)); | ||
420 | up_read(&fw_bus_type.subsys.rwsem); | ||
421 | |||
422 | return device; | ||
423 | } | ||
424 | |||
410 | static void fw_device_shutdown(struct work_struct *work) | 425 | static void fw_device_shutdown(struct work_struct *work) |
411 | { | 426 | { |
412 | struct fw_device *device = | 427 | struct fw_device *device = |
413 | container_of(work, struct fw_device, work.work); | 428 | container_of(work, struct fw_device, work.work); |
429 | int minor = MINOR(device->device.devt); | ||
430 | |||
431 | down_write(&fw_bus_type.subsys.rwsem); | ||
432 | idr_remove(&fw_device_idr, minor); | ||
433 | up_write(&fw_bus_type.subsys.rwsem); | ||
414 | 434 | ||
415 | device_remove_file(&device->device, &config_rom_attribute); | 435 | device_remove_file(&device->device, &config_rom_attribute); |
416 | cdev_del(&device->cdev); | ||
417 | unregister_chrdev_region(device->device.devt, 1); | ||
418 | device_for_each_child(&device->device, NULL, shutdown_unit); | 436 | device_for_each_child(&device->device, NULL, shutdown_unit); |
419 | device_unregister(&device->device); | 437 | device_unregister(&device->device); |
420 | } | 438 | } |
@@ -434,9 +452,9 @@ static void fw_device_shutdown(struct work_struct *work) | |||
434 | 452 | ||
435 | static void fw_device_init(struct work_struct *work) | 453 | static void fw_device_init(struct work_struct *work) |
436 | { | 454 | { |
437 | static atomic_t serial = ATOMIC_INIT(-1); | ||
438 | struct fw_device *device = | 455 | struct fw_device *device = |
439 | container_of(work, struct fw_device, work.work); | 456 | container_of(work, struct fw_device, work.work); |
457 | int minor, err; | ||
440 | 458 | ||
441 | /* All failure paths here set node->data to NULL, so that we | 459 | /* All failure paths here set node->data to NULL, so that we |
442 | * don't try to do device_for_each_child() on a kfree()'d | 460 | * 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 | return; | 474 | return; |
457 | } | 475 | } |
458 | 476 | ||
477 | err = -ENOMEM; | ||
478 | down_write(&fw_bus_type.subsys.rwsem); | ||
479 | if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) | ||
480 | err = idr_get_new(&fw_device_idr, device, &minor); | ||
481 | up_write(&fw_bus_type.subsys.rwsem); | ||
482 | if (err < 0) | ||
483 | goto error; | ||
484 | |||
459 | device->device.bus = &fw_bus_type; | 485 | device->device.bus = &fw_bus_type; |
460 | device->device.release = fw_device_release; | 486 | device->device.release = fw_device_release; |
461 | device->device.parent = device->card->device; | 487 | device->device.parent = device->card->device; |
488 | device->device.devt = MKDEV(fw_cdev_major, minor); | ||
462 | snprintf(device->device.bus_id, sizeof device->device.bus_id, | 489 | snprintf(device->device.bus_id, sizeof device->device.bus_id, |
463 | "fw%d", atomic_inc_return(&serial)); | 490 | "fw%d", minor); |
464 | |||
465 | if (alloc_chrdev_region(&device->device.devt, 0, 1, "fw")) { | ||
466 | fw_error("Failed to register char device region.\n"); | ||
467 | goto error; | ||
468 | } | ||
469 | |||
470 | cdev_init(&device->cdev, &fw_device_ops); | ||
471 | device->cdev.owner = THIS_MODULE; | ||
472 | kobject_set_name(&device->cdev.kobj, device->device.bus_id); | ||
473 | if (cdev_add(&device->cdev, device->device.devt, 1)) { | ||
474 | fw_error("Failed to register char device.\n"); | ||
475 | goto error; | ||
476 | } | ||
477 | 491 | ||
478 | if (device_add(&device->device)) { | 492 | if (device_add(&device->device)) { |
479 | fw_error("Failed to add device.\n"); | 493 | fw_error("Failed to add device.\n"); |
480 | goto error; | 494 | goto error_with_cdev; |
481 | } | 495 | } |
482 | 496 | ||
483 | if (device_create_file(&device->device, &config_rom_attribute) < 0) { | 497 | if (device_create_file(&device->device, &config_rom_attribute) < 0) { |
@@ -513,9 +527,11 @@ static void fw_device_init(struct work_struct *work) | |||
513 | 527 | ||
514 | error_with_device: | 528 | error_with_device: |
515 | device_del(&device->device); | 529 | device_del(&device->device); |
530 | error_with_cdev: | ||
531 | down_write(&fw_bus_type.subsys.rwsem); | ||
532 | idr_remove(&fw_device_idr, minor); | ||
533 | up_write(&fw_bus_type.subsys.rwsem); | ||
516 | error: | 534 | error: |
517 | cdev_del(&device->cdev); | ||
518 | unregister_chrdev_region(device->device.devt, 1); | ||
519 | put_device(&device->device); | 535 | put_device(&device->device); |
520 | } | 536 | } |
521 | 537 | ||
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index 4f731c2b121c..1a3655bea335 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h | |||
@@ -39,7 +39,7 @@ struct fw_device { | |||
39 | int generation; | 39 | int generation; |
40 | struct fw_card *card; | 40 | struct fw_card *card; |
41 | struct device device; | 41 | struct device device; |
42 | struct cdev cdev; | 42 | struct list_head link; |
43 | struct list_head client_list; | 43 | struct list_head client_list; |
44 | __be32 *config_rom; | 44 | __be32 *config_rom; |
45 | size_t config_rom_length; | 45 | size_t config_rom_length; |
@@ -59,6 +59,9 @@ int fw_device_enable_phys_dma(struct fw_device *device); | |||
59 | 59 | ||
60 | void fw_device_cdev_update(struct fw_device *device); | 60 | void fw_device_cdev_update(struct fw_device *device); |
61 | 61 | ||
62 | struct fw_device *fw_device_from_devt(dev_t devt); | ||
63 | extern int fw_cdev_major; | ||
64 | |||
62 | struct fw_unit { | 65 | struct fw_unit { |
63 | struct device device; | 66 | struct device device; |
64 | u32 *directory; | 67 | u32 *directory; |
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 8e2b94557390..3052698c13a6 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c | |||
@@ -761,6 +761,12 @@ static int __init fw_core_init(void) | |||
761 | if (retval < 0) | 761 | if (retval < 0) |
762 | return retval; | 762 | return retval; |
763 | 763 | ||
764 | fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); | ||
765 | if (fw_cdev_major < 0) { | ||
766 | bus_unregister(&fw_bus_type); | ||
767 | return fw_cdev_major; | ||
768 | } | ||
769 | |||
764 | /* Add the vendor textual descriptor. */ | 770 | /* Add the vendor textual descriptor. */ |
765 | retval = fw_core_add_descriptor(&vendor_id_descriptor); | 771 | retval = fw_core_add_descriptor(&vendor_id_descriptor); |
766 | BUG_ON(retval < 0); | 772 | BUG_ON(retval < 0); |
@@ -772,6 +778,7 @@ static int __init fw_core_init(void) | |||
772 | 778 | ||
773 | static void __exit fw_core_cleanup(void) | 779 | static void __exit fw_core_cleanup(void) |
774 | { | 780 | { |
781 | unregister_chrdev(fw_cdev_major, "firewire"); | ||
775 | bus_unregister(&fw_bus_type); | 782 | bus_unregister(&fw_bus_type); |
776 | } | 783 | } |
777 | 784 | ||