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 /drivers/firewire/fw-device.c | |
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>
Diffstat (limited to 'drivers/firewire/fw-device.c')
-rw-r--r-- | drivers/firewire/fw-device.c | 56 |
1 files changed, 36 insertions, 20 deletions
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 | ||