aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2009-01-17 16:45:54 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-01-20 13:29:52 -0500
commit3d36a0df3b473fb53531484df227f2da8bc7494b (patch)
treef0fcb758c8e68f1e5389ccd895d98efcc55ed0e8 /drivers
parent8cd0bbbdff7471163cc6a058be8b8610ddd01d6b (diff)
firewire: keep highlevel drivers attached during brief connection loss
There are situations when nodes vanish from the bus and come back quickly thereafter: - When certain bus-powered hubs are plugged in, - when certain devices are plugged into 6-port hubs, - when certain disk enclosures are switched from self-power to bus power or vice versa and break the daisy chain during the transition, - when the user plugs a cable out and quickly plugs it back in, e.g. to reorder a daisy chain (works on Mac OS X if done quickly enough), - when certain hubs temporarily malfunction during high bus traffic. Until now, firewire-core reported affected nodes as lost to the highlevel drivers (firewire-sbp2 and userspace drivers). We now delay the destruction of device representations until after at least two seconds after the last bus reset. If a "new" device is detected in this period whose bus information block and root directory header match that of a device which is pending for deletion, we resurrect that device and send update calls to highlevel drivers. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firewire/fw-device.c121
-rw-r--r--drivers/firewire/fw-device.h1
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
637static void fw_device_shutdown(struct work_struct *work) 654static 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
685static 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 */
693static 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
672static void fw_device_init(struct work_struct *work) 737static 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 @@
28enum fw_device_state { 28enum 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