diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-01-31 11:32:31 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-05 16:30:01 -0500 |
commit | 9d45cf9e36bf9bcf16df6e1cbf049807c8402823 (patch) | |
tree | 2118a16701418af10d215d2174df7ee0a5cbe6bd /drivers/firewire/fw-device.c | |
parent | a146649bc19d5eba4f5bfac6720c5f252d517a71 (diff) | |
parent | 0cd5c3c80a0ebd68c08312fa7d8c13149cc61c4c (diff) |
Merge branch 'x86/urgent' into x86/apic
Conflicts:
arch/x86/mach-default/setup.c
Semantic merge:
arch/x86/kernel/irqinit_32.c
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/firewire/fw-device.c')
-rw-r--r-- | drivers/firewire/fw-device.c | 123 |
1 files changed, 101 insertions, 22 deletions
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 2af5a8d1e012..bf53acb45652 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,39 @@ 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 | && !list_empty(&device->card->link)) { | ||
662 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | if (atomic_cmpxchg(&device->state, | ||
667 | FW_DEVICE_GONE, | ||
668 | FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE) | ||
669 | return; | ||
670 | |||
643 | fw_device_cdev_remove(device); | 671 | fw_device_cdev_remove(device); |
644 | device_for_each_child(&device->device, NULL, shutdown_unit); | 672 | device_for_each_child(&device->device, NULL, shutdown_unit); |
645 | device_unregister(&device->device); | 673 | device_unregister(&device->device); |
@@ -647,6 +675,7 @@ static void fw_device_shutdown(struct work_struct *work) | |||
647 | down_write(&fw_device_rwsem); | 675 | down_write(&fw_device_rwsem); |
648 | idr_remove(&fw_device_idr, minor); | 676 | idr_remove(&fw_device_idr, minor); |
649 | up_write(&fw_device_rwsem); | 677 | up_write(&fw_device_rwsem); |
678 | |||
650 | fw_device_put(device); | 679 | fw_device_put(device); |
651 | } | 680 | } |
652 | 681 | ||
@@ -654,25 +683,63 @@ static struct device_type fw_device_type = { | |||
654 | .release = fw_device_release, | 683 | .release = fw_device_release, |
655 | }; | 684 | }; |
656 | 685 | ||
686 | static void fw_device_update(struct work_struct *work); | ||
687 | |||
657 | /* | 688 | /* |
658 | * These defines control the retry behavior for reading the config | 689 | * 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 | 690 | * 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 | 691 | * device, revive the existing fw_device. |
661 | * going to respond at all. As for the initial delay, a lot of | 692 | * 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 | */ | 693 | */ |
694 | static int lookup_existing_device(struct device *dev, void *data) | ||
695 | { | ||
696 | struct fw_device *old = fw_device(dev); | ||
697 | struct fw_device *new = data; | ||
698 | struct fw_card *card = new->card; | ||
699 | int match = 0; | ||
700 | |||
701 | down_read(&fw_device_rwsem); /* serialize config_rom access */ | ||
702 | spin_lock_irq(&card->lock); /* serialize node access */ | ||
703 | |||
704 | if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && | ||
705 | atomic_cmpxchg(&old->state, | ||
706 | FW_DEVICE_GONE, | ||
707 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { | ||
708 | struct fw_node *current_node = new->node; | ||
709 | struct fw_node *obsolete_node = old->node; | ||
710 | |||
711 | new->node = obsolete_node; | ||
712 | new->node->data = new; | ||
713 | old->node = current_node; | ||
714 | old->node->data = old; | ||
715 | |||
716 | old->max_speed = new->max_speed; | ||
717 | old->node_id = current_node->node_id; | ||
718 | smp_wmb(); /* update node_id before generation */ | ||
719 | old->generation = card->generation; | ||
720 | old->config_rom_retries = 0; | ||
721 | fw_notify("rediscovered device %s\n", dev_name(dev)); | ||
667 | 722 | ||
668 | #define MAX_RETRIES 10 | 723 | PREPARE_DELAYED_WORK(&old->work, fw_device_update); |
669 | #define RETRY_DELAY (3 * HZ) | 724 | schedule_delayed_work(&old->work, 0); |
670 | #define INITIAL_DELAY (HZ / 2) | 725 | |
726 | if (current_node == card->root_node) | ||
727 | fw_schedule_bm_work(card, 0); | ||
728 | |||
729 | match = 1; | ||
730 | } | ||
731 | |||
732 | spin_unlock_irq(&card->lock); | ||
733 | up_read(&fw_device_rwsem); | ||
734 | |||
735 | return match; | ||
736 | } | ||
671 | 737 | ||
672 | static void fw_device_init(struct work_struct *work) | 738 | static void fw_device_init(struct work_struct *work) |
673 | { | 739 | { |
674 | struct fw_device *device = | 740 | struct fw_device *device = |
675 | container_of(work, struct fw_device, work.work); | 741 | container_of(work, struct fw_device, work.work); |
742 | struct device *revived_dev; | ||
676 | int minor, err; | 743 | int minor, err; |
677 | 744 | ||
678 | /* | 745 | /* |
@@ -696,6 +763,15 @@ static void fw_device_init(struct work_struct *work) | |||
696 | return; | 763 | return; |
697 | } | 764 | } |
698 | 765 | ||
766 | revived_dev = device_find_child(device->card->device, | ||
767 | device, lookup_existing_device); | ||
768 | if (revived_dev) { | ||
769 | put_device(revived_dev); | ||
770 | fw_device_release(&device->device); | ||
771 | |||
772 | return; | ||
773 | } | ||
774 | |||
699 | device_initialize(&device->device); | 775 | device_initialize(&device->device); |
700 | 776 | ||
701 | fw_device_get(device); | 777 | fw_device_get(device); |
@@ -734,9 +810,10 @@ static void fw_device_init(struct work_struct *work) | |||
734 | * fw_node_event(). | 810 | * fw_node_event(). |
735 | */ | 811 | */ |
736 | if (atomic_cmpxchg(&device->state, | 812 | if (atomic_cmpxchg(&device->state, |
737 | FW_DEVICE_INITIALIZING, | 813 | FW_DEVICE_INITIALIZING, |
738 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { | 814 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { |
739 | fw_device_shutdown(work); | 815 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
816 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
740 | } else { | 817 | } else { |
741 | if (device->config_rom_retries) | 818 | if (device->config_rom_retries) |
742 | fw_notify("created device %s: GUID %08x%08x, S%d00, " | 819 | fw_notify("created device %s: GUID %08x%08x, S%d00, " |
@@ -847,8 +924,8 @@ static void fw_device_refresh(struct work_struct *work) | |||
847 | 924 | ||
848 | case REREAD_BIB_UNCHANGED: | 925 | case REREAD_BIB_UNCHANGED: |
849 | if (atomic_cmpxchg(&device->state, | 926 | if (atomic_cmpxchg(&device->state, |
850 | FW_DEVICE_INITIALIZING, | 927 | FW_DEVICE_INITIALIZING, |
851 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | 928 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) |
852 | goto gone; | 929 | goto gone; |
853 | 930 | ||
854 | fw_device_update(work); | 931 | fw_device_update(work); |
@@ -879,8 +956,8 @@ static void fw_device_refresh(struct work_struct *work) | |||
879 | create_units(device); | 956 | create_units(device); |
880 | 957 | ||
881 | if (atomic_cmpxchg(&device->state, | 958 | if (atomic_cmpxchg(&device->state, |
882 | FW_DEVICE_INITIALIZING, | 959 | FW_DEVICE_INITIALIZING, |
883 | FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) | 960 | FW_DEVICE_RUNNING) == FW_DEVICE_GONE) |
884 | goto gone; | 961 | goto gone; |
885 | 962 | ||
886 | fw_notify("refreshed device %s\n", dev_name(&device->device)); | 963 | fw_notify("refreshed device %s\n", dev_name(&device->device)); |
@@ -890,8 +967,9 @@ static void fw_device_refresh(struct work_struct *work) | |||
890 | give_up: | 967 | give_up: |
891 | fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); | 968 | fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); |
892 | gone: | 969 | gone: |
893 | atomic_set(&device->state, FW_DEVICE_SHUTDOWN); | 970 | atomic_set(&device->state, FW_DEVICE_GONE); |
894 | fw_device_shutdown(work); | 971 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
972 | schedule_delayed_work(&device->work, SHUTDOWN_DELAY); | ||
895 | out: | 973 | out: |
896 | if (node_id == card->root_node->node_id) | 974 | if (node_id == card->root_node->node_id) |
897 | fw_schedule_bm_work(card, 0); | 975 | fw_schedule_bm_work(card, 0); |
@@ -995,9 +1073,10 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) | |||
995 | */ | 1073 | */ |
996 | device = node->data; | 1074 | device = node->data; |
997 | if (atomic_xchg(&device->state, | 1075 | if (atomic_xchg(&device->state, |
998 | FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) { | 1076 | FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { |
999 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); | 1077 | PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); |
1000 | schedule_delayed_work(&device->work, 0); | 1078 | schedule_delayed_work(&device->work, |
1079 | list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); | ||
1001 | } | 1080 | } |
1002 | break; | 1081 | break; |
1003 | } | 1082 | } |