diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/firewire/fw-device.c | 121 | ||||
| -rw-r--r-- | drivers/firewire/fw-device.h | 1 |
2 files changed, 100 insertions, 22 deletions
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 2af5a8d1e012..0925d91b2610 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/device.h> | 25 | #include <linux/device.h> |
| 26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
| 27 | #include <linux/idr.h> | 27 | #include <linux/idr.h> |
| 28 | #include <linux/jiffies.h> | ||
| 28 | #include <linux/string.h> | 29 | #include <linux/string.h> |
| 29 | #include <linux/rwsem.h> | 30 | #include <linux/rwsem.h> |
| 30 | #include <linux/semaphore.h> | 31 | #include <linux/semaphore.h> |
| @@ -634,12 +635,38 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) | |||
| 634 | return device; | 635 | return device; |
| 635 | } | 636 | } |
| 636 | 637 | ||
| 638 | /* | ||
| 639 | * These defines control the retry behavior for reading the config | ||
| 640 | * rom. It shouldn't be necessary to tweak these; if the device | ||
| 641 | * doesn't respond to a config rom read within 10 seconds, it's not | ||
| 642 | * going to respond at all. As for the initial delay, a lot of | ||
| 643 | * devices will be able to respond within half a second after bus | ||
| 644 | * reset. On the other hand, it's not really worth being more | ||
| 645 | * aggressive than that, since it scales pretty well; if 10 devices | ||
| 646 | * are plugged in, they're all getting read within one second. | ||
| 647 | */ | ||
| 648 | |||
| 649 | #define MAX_RETRIES 10 | ||
| 650 | #define RETRY_DELAY (3 * HZ) | ||
| 651 | #define INITIAL_DELAY (HZ / 2) | ||
| 652 | #define SHUTDOWN_DELAY (2 * HZ) | ||
| 653 | |||
| 637 | static void fw_device_shutdown(struct work_struct *work) | 654 | static void fw_device_shutdown(struct work_struct *work) |
| 638 | { | 655 | { |
| 639 | struct fw_device *device = | 656 | struct fw_device *device = |
| 640 | container_of(work, struct fw_device, work.work); | 657 | container_of(work, struct fw_device, work.work); |
| 641 | int minor = MINOR(device->device.devt); | 658 | int minor = MINOR(device->device.devt); |
| 642 | 659 | ||
| 660 | if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY)) { | ||
| 661 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
| 662 | return; | ||
| 663 | } | ||
| 664 | |||
| 665 | if (atomic_cmpxchg(&device->state, | ||
| 666 | FW_DEVICE_GONE, | ||
| 667 | FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE) | ||
| 668 | return; | ||
| 669 | |||
| 643 | fw_device_cdev_remove(device); | 670 | fw_device_cdev_remove(device); |
| 644 | device_for_each_child(&device->device, NULL, shutdown_unit); | 671 | device_for_each_child(&device->device, NULL, shutdown_unit); |
| 645 | device_unregister(&device->device); | 672 | device_unregister(&device->device); |
| @@ -647,6 +674,7 @@ static void fw_device_shutdown(struct work_struct *work) | |||
| 647 | down_write(&fw_device_rwsem); | 674 | down_write(&fw_device_rwsem); |
| 648 | idr_remove(&fw_device_idr, minor); | 675 | idr_remove(&fw_device_idr, minor); |
| 649 | up_write(&fw_device_rwsem); | 676 | up_write(&fw_device_rwsem); |
| 677 | |||
| 650 | fw_device_put(device); | 678 | fw_device_put(device); |
| 651 | } | 679 | } |
| 652 | 680 | ||
| @@ -654,25 +682,63 @@ static struct device_type fw_device_type = { | |||
| 654 | .release = fw_device_release, | 682 | .release = fw_device_release, |
| 655 | }; | 683 | }; |
| 656 | 684 | ||
| 685 | static void fw_device_update(struct work_struct *work); | ||
| 686 | |||
| 657 | /* | 687 | /* |
| 658 | * These defines control the retry behavior for reading the config | 688 | * If a device was pending for deletion because its node went away but its |
| 659 | * rom. It shouldn't be necessary to tweak these; if the device | 689 | * bus info block and root directory header matches that of a newly discovered |
| 660 | * doesn't respond to a config rom read within 10 seconds, it's not | 690 | * device, revive the existing fw_device. |
| 661 | * going to respond at all. As for the initial delay, a lot of | 691 | * The newly allocated fw_device becomes obsolete instead. |
| 662 | * devices will be able to respond within half a second after bus | ||
| 663 | * reset. On the other hand, it's not really worth being more | ||
| 664 | * aggressive than that, since it scales pretty well; if 10 devices | ||
| 665 | * are plugged in, they're all getting read within one second. | ||
| 666 | */ | 692 | */ |
| 693 | static int lookup_existing_device(struct device *dev, void *data) | ||
| 694 | { | ||
| 695 | struct fw_device *old = fw_device(dev); | ||
| 696 | struct fw_device *new = data; | ||
| 697 | struct fw_card *card = new->card; | ||
| 698 | int match = 0; | ||
| 699 | |||
| 700 | down_read(&fw_device_rwsem); /* serialize config_rom access */ | ||
| 701 | spin_lock_irq(&card->lock); /* serialize node access */ | ||
| 702 | |||
| 703 | if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && | ||
| 704 | atomic_cmpxchg(&old->state, | ||
| 705 | FW_DEVICE_GONE, | ||
| 706 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { | ||
| 707 | struct fw_node *current_node = new->node; | ||
| 708 | struct fw_node *obsolete_node = old->node; | ||
| 709 | |||
| 710 | new->node = obsolete_node; | ||
| 711 | new->node->data = new; | ||
| 712 | old->node = current_node; | ||
| 713 | old->node->data = old; | ||
| 714 | |||
| 715 | old->max_speed = new->max_speed; | ||
| 716 | old->node_id = current_node->node_id; | ||
| 717 | smp_wmb(); /* update node_id before generation */ | ||
| 718 | old->generation = card->generation; | ||
| 719 | old->config_rom_retries = 0; | ||
| 720 | fw_notify("rediscovered device %s\n", dev_name(dev)); | ||
| 667 | 721 | ||
| 668 | #define MAX_RETRIES 10 | 722 | PREPARE_DELAYED_WORK(&old->work, fw_device_update); |
| 669 | #define RETRY_DELAY (3 * HZ) | 723 | schedule_delayed_work(&old->work, 0); |
| 670 | #define INITIAL_DELAY (HZ / 2) | 724 | |
| 725 | if (current_node == card->root_node) | ||
| 726 | fw_schedule_bm_work(card, 0); | ||
| 727 | |||
| 728 | match = 1; | ||
| 729 | } | ||
| 730 | |||
| 731 | spin_unlock_irq(&card->lock); | ||
| 732 | up_read(&fw_device_rwsem); | ||
| 733 | |||
| 734 | return match; | ||
| 735 | } | ||
| 671 | 736 | ||
| 672 | static void fw_device_init(struct work_struct *work) | 737 | static void fw_device_init(struct work_struct *work) |
| 673 | { | 738 | { |
| 674 | struct fw_device *device = | 739 | struct fw_device *device = |
| 675 | container_of(work, struct fw_device, work.work); | 740 | container_of(work, struct fw_device, work.work); |
| 741 | struct device *revived_dev; | ||
| 676 | int minor, err; | 742 | int minor, err; |
| 677 | 743 | ||
| 678 | /* | 744 | /* |
| @@ -696,6 +762,15 @@ static void fw_device_init(struct work_struct *work) | |||
| 696 | return; | 762 | return; |
| 697 | } | 763 | } |
| 698 | 764 | ||
| 765 | revived_dev = device_find_child(device->card->device, | ||
| 766 | device, lookup_existing_device); | ||
| 767 | if (revived_dev) { | ||
| 768 | put_device(revived_dev); | ||
| 769 | fw_device_release(&device->device); | ||
| 770 | |||
| 771 | return; | ||
| 772 | } | ||
| 773 | |||
| 699 | device_initialize(&device->device); | 774 | device_initialize(&device->device); |
| 700 | 775 | ||
| 701 | fw_device_get(device); | 776 | fw_device_get(device); |
| @@ -734,9 +809,10 @@ static void fw_device_init(struct work_struct *work) | |||
| 734 | * fw_node_event(). | 809 | * fw_node_event(). |
| 735 | */ | 810 | */ |
| 736 | if (atomic_cmpxchg(&device->state, | 811 | if (atomic_cmpxchg(&device->state, |
| 737 | FW_DEVICE_INITIALIZING, | 812 | FW_DEVICE_INITIALIZING, |
| 738 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { | 813 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { |
| 739 | fw_device_shutdown(work); | 814 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
| 815 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
| 740 | } else { | 816 | } else { |
| 741 | if (device->config_rom_retries) | 817 | if (device->config_rom_retries) |
| 742 | fw_notify("created device %s: GUID %08x%08x, S%d00, " | 818 | fw_notify("created device %s: GUID %08x%08x, S%d00, " |
| @@ -847,8 +923,8 @@ static void fw_device_refresh(struct work_struct *work) | |||
| 847 | 923 | ||
| 848 | case REREAD_BIB_UNCHANGED: | 924 | case REREAD_BIB_UNCHANGED: |
| 849 | if (atomic_cmpxchg(&device->state, | 925 | if (atomic_cmpxchg(&device->state, |
| 850 | FW_DEVICE_INITIALIZING, | 926 | FW_DEVICE_INITIALIZING, |
| 851 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | 927 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) |
| 852 | goto gone; | 928 | goto gone; |
| 853 | 929 | ||
| 854 | fw_device_update(work); | 930 | fw_device_update(work); |
| @@ -879,8 +955,8 @@ static void fw_device_refresh(struct work_struct *work) | |||
| 879 | create_units(device); | 955 | create_units(device); |
| 880 | 956 | ||
| 881 | if (atomic_cmpxchg(&device->state, | 957 | if (atomic_cmpxchg(&device->state, |
| 882 | FW_DEVICE_INITIALIZING, | 958 | FW_DEVICE_INITIALIZING, |
| 883 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | 959 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) |
| 884 | goto gone; | 960 | goto gone; |
| 885 | 961 | ||
| 886 | fw_notify("refreshed device %s\n", dev_name(&device->device)); | 962 | fw_notify("refreshed device %s\n", dev_name(&device->device)); |
| @@ -890,8 +966,9 @@ static void fw_device_refresh(struct work_struct *work) | |||
| 890 | give_up: | 966 | give_up: |
| 891 | fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); | 967 | fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); |
| 892 | gone: | 968 | gone: |
| 893 | atomic_set(&device->state, FW_DEVICE_SHUTDOWN); | 969 | atomic_set(&device->state, FW_DEVICE_GONE); |
| 894 | fw_device_shutdown(work); | 970 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
| 971 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
| 895 | out: | 972 | out: |
| 896 | if (node_id == card->root_node->node_id) | 973 | if (node_id == card->root_node->node_id) |
| 897 | fw_schedule_bm_work(card, 0); | 974 | fw_schedule_bm_work(card, 0); |
| @@ -995,9 +1072,9 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
| 995 | */ | 1072 | */ |
| 996 | device = node->data; | 1073 | device = node->data; |
| 997 | if (atomic_xchg(&device->state, | 1074 | if (atomic_xchg(&device->state, |
| 998 | FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) { | 1075 | FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { |
| 999 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); | 1076 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
| 1000 | schedule_delayed_work(&device->work, 0); | 1077 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); |
| 1001 | } | 1078 | } |
| 1002 | break; | 1079 | break; |
| 1003 | } | 1080 | } |
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index df51732608d9..8ef6ec2ca21c 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | enum fw_device_state { | 28 | enum fw_device_state { |
| 29 | FW_DEVICE_INITIALIZING, | 29 | FW_DEVICE_INITIALIZING, |
| 30 | FW_DEVICE_RUNNING, | 30 | FW_DEVICE_RUNNING, |
| 31 | FW_DEVICE_GONE, | ||
| 31 | FW_DEVICE_SHUTDOWN, | 32 | FW_DEVICE_SHUTDOWN, |
| 32 | }; | 33 | }; |
| 33 | 34 | ||
