aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-11-17 20:59:51 -0500
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 14:52:34 -0500
commit87c8331fcf72e501c3a3c0cdc5c9391ec72f7cf2 (patch)
tree4ed0e98760c977010fe54778c1a25625840b4583 /drivers/scsi
parente139942d77a6e3ac83bc322e826668054a8601d6 (diff)
[SCSI] libsas: prevent domain rediscovery competing with ata error handling
libata error handling provides for a timeout for link recovery. libsas must not rescan for previously known devices in this interval otherwise it may remove a device that is simply waiting for its link to recover. Let libata-eh make the determination of when the link is stable and prevent libsas (host workqueue) from taking action while this determination is pending. Using a mutex (ha->disco_mutex) to flush and disable revalidation while eh is running requires any discovery action that may block on eh be moved to its own context outside the lock. Probing ATA devices explicitly waits on ata-eh and the cache-flush-io issued during device removal may also pend awaiting eh completion. Essentially any rphy add/remove activity needs to run outside the lock. This adds two new cleanup states for sas_unregister_domain_devices() 'allocated-but-not-probed', and 'flagged-for-destruction'. In the 'allocated-but-not-probed' state dev->rphy points to a rphy that is known to have not been through a sas_rphy_add() event. At domain teardown check if this device is still pending probe and cleanup accordingly. Similarly if a device has already been queued for removal then sas_unregister_domain_devices has nothing to do. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/libsas/sas_ata.c55
-rw-r--r--drivers/scsi/libsas/sas_discover.c63
-rw-r--r--drivers/scsi/libsas/sas_event.c26
-rw-r--r--drivers/scsi/libsas/sas_expander.c5
-rw-r--r--drivers/scsi/libsas/sas_init.c2
-rw-r--r--drivers/scsi/libsas/sas_internal.h3
-rw-r--r--drivers/scsi/libsas/sas_port.c2
-rw-r--r--drivers/scsi/scsi_transport_sas.c18
8 files changed, 158 insertions, 16 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 2fc5a3961ca6..4b6365c6410f 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -758,6 +758,35 @@ static int sas_discover_sata_pm(struct domain_device *dev)
758 return -ENODEV; 758 return -ENODEV;
759} 759}
760 760
761void sas_probe_sata(struct work_struct *work)
762{
763 struct domain_device *dev, *n;
764 struct sas_discovery_event *ev =
765 container_of(work, struct sas_discovery_event, work);
766 struct asd_sas_port *port = ev->port;
767
768 clear_bit(DISCE_PROBE, &port->disc.pending);
769
770 list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
771 int err;
772
773 spin_lock_irq(&port->dev_list_lock);
774 list_add_tail(&dev->dev_list_node, &port->dev_list);
775 spin_unlock_irq(&port->dev_list_lock);
776
777 err = sas_rphy_add(dev->rphy);
778
779 if (err) {
780 SAS_DPRINTK("%s: for %s device %16llx returned %d\n",
781 __func__, dev->parent ? "exp-attached" :
782 "direct-attached",
783 SAS_ADDR(dev->sas_addr), err);
784 sas_unregister_dev(port, dev);
785 } else
786 list_del_init(&dev->disco_list_node);
787 }
788}
789
761/** 790/**
762 * sas_discover_sata -- discover an STP/SATA domain device 791 * sas_discover_sata -- discover an STP/SATA domain device
763 * @dev: pointer to struct domain_device of interest 792 * @dev: pointer to struct domain_device of interest
@@ -794,10 +823,15 @@ int sas_discover_sata(struct domain_device *dev)
794 break; 823 break;
795 } 824 }
796 sas_notify_lldd_dev_gone(dev); 825 sas_notify_lldd_dev_gone(dev);
797 if (!res) { 826
798 sas_notify_lldd_dev_found(dev); 827 if (res)
799 res = sas_rphy_add(dev->rphy); 828 return res;
800 } 829
830 res = sas_notify_lldd_dev_found(dev);
831 if (res)
832 return res;
833
834 sas_discover_event(dev->port, DISCE_PROBE);
801 835
802 return res; 836 return res;
803} 837}
@@ -805,6 +839,17 @@ int sas_discover_sata(struct domain_device *dev)
805void sas_ata_strategy_handler(struct Scsi_Host *shost) 839void sas_ata_strategy_handler(struct Scsi_Host *shost)
806{ 840{
807 struct scsi_device *sdev; 841 struct scsi_device *sdev;
842 struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
843
844 /* it's ok to defer revalidation events during ata eh, these
845 * disks are in one of three states:
846 * 1/ present for initial domain discovery, and these
847 * resets will cause bcn flutters
848 * 2/ hot removed, we'll discover that after eh fails
849 * 3/ hot added after initial discovery, lost the race, and need
850 * to catch the next train.
851 */
852 sas_disable_revalidation(sas_ha);
808 853
809 shost_for_each_device(sdev, shost) { 854 shost_for_each_device(sdev, shost) {
810 struct domain_device *ddev = sdev_to_domain_dev(sdev); 855 struct domain_device *ddev = sdev_to_domain_dev(sdev);
@@ -816,6 +861,8 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
816 ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); 861 ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler");
817 ata_scsi_port_error_handler(shost, ap); 862 ata_scsi_port_error_handler(shost, ap);
818 } 863 }
864
865 sas_enable_revalidation(sas_ha);
819} 866}
820 867
821int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, 868int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 32e011766046..7e8fdcb202b7 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -148,9 +148,14 @@ static int sas_get_port_device(struct asd_sas_port *port)
148 port->disc.max_level = 0; 148 port->disc.max_level = 0;
149 149
150 dev->rphy = rphy; 150 dev->rphy = rphy;
151 spin_lock_irq(&port->dev_list_lock); 151
152 list_add_tail(&dev->dev_list_node, &port->dev_list); 152 if (dev_is_sata(dev))
153 spin_unlock_irq(&port->dev_list_lock); 153 list_add_tail(&dev->disco_list_node, &port->disco_list);
154 else {
155 spin_lock_irq(&port->dev_list_lock);
156 list_add_tail(&dev->dev_list_node, &port->dev_list);
157 spin_unlock_irq(&port->dev_list_lock);
158 }
154 159
155 return 0; 160 return 0;
156} 161}
@@ -255,14 +260,43 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
255 sas_put_device(dev); 260 sas_put_device(dev);
256} 261}
257 262
258void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) 263static void sas_destruct_devices(struct work_struct *work)
259{ 264{
260 if (dev->rphy) { 265 struct domain_device *dev, *n;
266 struct sas_discovery_event *ev =
267 container_of(work, struct sas_discovery_event, work);
268 struct asd_sas_port *port = ev->port;
269
270 clear_bit(DISCE_DESTRUCT, &port->disc.pending);
271
272 list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {
273 list_del_init(&dev->disco_list_node);
274
261 sas_remove_children(&dev->rphy->dev); 275 sas_remove_children(&dev->rphy->dev);
262 sas_rphy_delete(dev->rphy); 276 sas_rphy_delete(dev->rphy);
263 dev->rphy = NULL; 277 dev->rphy = NULL;
278 sas_unregister_common_dev(port, dev);
279
280 sas_put_device(dev);
281 }
282}
283
284void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
285{
286 if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
287 !list_empty(&dev->disco_list_node)) {
288 /* this rphy never saw sas_rphy_add */
289 list_del_init(&dev->disco_list_node);
290 sas_rphy_free(dev->rphy);
291 dev->rphy = NULL;
292 sas_unregister_common_dev(port, dev);
293 }
294
295 if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
296 sas_rphy_unlink(dev->rphy);
297 list_move_tail(&dev->disco_list_node, &port->destroy_list);
298 sas_discover_event(dev->port, DISCE_DESTRUCT);
264 } 299 }
265 sas_unregister_common_dev(port, dev);
266} 300}
267 301
268void sas_unregister_domain_devices(struct asd_sas_port *port) 302void sas_unregister_domain_devices(struct asd_sas_port *port)
@@ -271,6 +305,8 @@ void sas_unregister_domain_devices(struct asd_sas_port *port)
271 305
272 list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) 306 list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node)
273 sas_unregister_dev(port, dev); 307 sas_unregister_dev(port, dev);
308 list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)
309 sas_unregister_dev(port, dev);
274 310
275 port->port->rphy = NULL; 311 port->port->rphy = NULL;
276 312
@@ -335,6 +371,7 @@ static void sas_discover_domain(struct work_struct *work)
335 sas_rphy_free(dev->rphy); 371 sas_rphy_free(dev->rphy);
336 dev->rphy = NULL; 372 dev->rphy = NULL;
337 373
374 list_del_init(&dev->disco_list_node);
338 spin_lock_irq(&port->dev_list_lock); 375 spin_lock_irq(&port->dev_list_lock);
339 list_del_init(&dev->dev_list_node); 376 list_del_init(&dev->dev_list_node);
340 spin_unlock_irq(&port->dev_list_lock); 377 spin_unlock_irq(&port->dev_list_lock);
@@ -353,16 +390,28 @@ static void sas_revalidate_domain(struct work_struct *work)
353 struct sas_discovery_event *ev = 390 struct sas_discovery_event *ev =
354 container_of(work, struct sas_discovery_event, work); 391 container_of(work, struct sas_discovery_event, work);
355 struct asd_sas_port *port = ev->port; 392 struct asd_sas_port *port = ev->port;
393 struct sas_ha_struct *ha = port->ha;
394
395 /* prevent revalidation from finding sata links in recovery */
396 mutex_lock(&ha->disco_mutex);
397 if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {
398 SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n",
399 port->id, task_pid_nr(current));
400 goto out;
401 }
356 402
357 clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending); 403 clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending);
358 404
359 SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, 405 SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
360 task_pid_nr(current)); 406 task_pid_nr(current));
407
361 if (port->port_dev) 408 if (port->port_dev)
362 res = sas_ex_revalidate_domain(port->port_dev); 409 res = sas_ex_revalidate_domain(port->port_dev);
363 410
364 SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", 411 SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
365 port->id, task_pid_nr(current), res); 412 port->id, task_pid_nr(current), res);
413 out:
414 mutex_unlock(&ha->disco_mutex);
366} 415}
367 416
368/* ---------- Events ---------- */ 417/* ---------- Events ---------- */
@@ -414,6 +463,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
414 static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { 463 static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
415 [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, 464 [DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
416 [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, 465 [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
466 [DISCE_PROBE] = sas_probe_sata,
467 [DISCE_DESTRUCT] = sas_destruct_devices,
417 }; 468 };
418 469
419 disc->pending = 0; 470 disc->pending = 0;
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c
index e5035aa4c2a6..933d757499b5 100644
--- a/drivers/scsi/libsas/sas_event.c
+++ b/drivers/scsi/libsas/sas_event.c
@@ -81,6 +81,32 @@ int sas_drain_work(struct sas_ha_struct *ha)
81} 81}
82EXPORT_SYMBOL_GPL(sas_drain_work); 82EXPORT_SYMBOL_GPL(sas_drain_work);
83 83
84void sas_disable_revalidation(struct sas_ha_struct *ha)
85{
86 mutex_lock(&ha->disco_mutex);
87 set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
88 mutex_unlock(&ha->disco_mutex);
89}
90
91void sas_enable_revalidation(struct sas_ha_struct *ha)
92{
93 int i;
94
95 mutex_lock(&ha->disco_mutex);
96 clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
97 for (i = 0; i < ha->num_phys; i++) {
98 struct asd_sas_port *port = ha->sas_port[i];
99 const int ev = DISCE_REVALIDATE_DOMAIN;
100 struct sas_discovery *d = &port->disc;
101
102 if (!test_and_clear_bit(ev, &d->pending))
103 continue;
104
105 sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha);
106 }
107 mutex_unlock(&ha->disco_mutex);
108}
109
84static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) 110static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
85{ 111{
86 BUG_ON(event >= HA_NUM_EVENTS); 112 BUG_ON(event >= HA_NUM_EVENTS);
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index f33d0c9911c4..e45b259dac4c 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -704,9 +704,7 @@ static struct domain_device *sas_ex_discover_end_dev(
704 704
705 child->rphy = rphy; 705 child->rphy = rphy;
706 706
707 spin_lock_irq(&parent->port->dev_list_lock); 707 list_add_tail(&child->disco_list_node, &parent->port->disco_list);
708 list_add_tail(&child->dev_list_node, &parent->port->dev_list);
709 spin_unlock_irq(&parent->port->dev_list_lock);
710 708
711 res = sas_discover_sata(child); 709 res = sas_discover_sata(child);
712 if (res) { 710 if (res) {
@@ -756,6 +754,7 @@ static struct domain_device *sas_ex_discover_end_dev(
756 sas_rphy_free(child->rphy); 754 sas_rphy_free(child->rphy);
757 child->rphy = NULL; 755 child->rphy = NULL;
758 756
757 list_del(&child->disco_list_node);
759 spin_lock_irq(&parent->port->dev_list_lock); 758 spin_lock_irq(&parent->port->dev_list_lock);
760 list_del(&child->dev_list_node); 759 list_del(&child->dev_list_node);
761 spin_unlock_irq(&parent->port->dev_list_lock); 760 spin_unlock_irq(&parent->port->dev_list_lock);
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 572b943d7603..52cd11d76664 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -104,6 +104,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
104{ 104{
105 int error = 0; 105 int error = 0;
106 106
107 mutex_init(&sas_ha->disco_mutex);
107 spin_lock_init(&sas_ha->phy_port_lock); 108 spin_lock_init(&sas_ha->phy_port_lock);
108 sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); 109 sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
109 110
@@ -168,6 +169,7 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha)
168 sas_drain_work(sas_ha); 169 sas_drain_work(sas_ha);
169 170
170 sas_unregister_ports(sas_ha); 171 sas_unregister_ports(sas_ha);
172 sas_drain_work(sas_ha);
171 173
172 if (sas_ha->lldd_max_execute_num > 1) { 174 if (sas_ha->lldd_max_execute_num > 1) {
173 sas_shutdown_queue(sas_ha); 175 sas_shutdown_queue(sas_ha);
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 948ea64cc2eb..ebe9b81ddef5 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -56,6 +56,8 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
56int sas_init_queue(struct sas_ha_struct *sas_ha); 56int sas_init_queue(struct sas_ha_struct *sas_ha);
57int sas_init_events(struct sas_ha_struct *sas_ha); 57int sas_init_events(struct sas_ha_struct *sas_ha);
58void sas_shutdown_queue(struct sas_ha_struct *sas_ha); 58void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
59void sas_disable_revalidation(struct sas_ha_struct *ha);
60void sas_enable_revalidation(struct sas_ha_struct *ha);
59 61
60void sas_deform_port(struct asd_sas_phy *phy, int gone); 62void sas_deform_port(struct asd_sas_phy *phy, int gone);
61 63
@@ -138,6 +140,7 @@ static inline struct domain_device *sas_alloc_device(void)
138 if (dev) { 140 if (dev) {
139 INIT_LIST_HEAD(&dev->siblings); 141 INIT_LIST_HEAD(&dev->siblings);
140 INIT_LIST_HEAD(&dev->dev_list_node); 142 INIT_LIST_HEAD(&dev->dev_list_node);
143 INIT_LIST_HEAD(&dev->disco_list_node);
141 kref_init(&dev->kref); 144 kref_init(&dev->kref);
142 } 145 }
143 return dev; 146 return dev;
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index d88e55f9732b..2980bde4e34a 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -277,6 +277,8 @@ static void sas_init_port(struct asd_sas_port *port,
277 memset(port, 0, sizeof(*port)); 277 memset(port, 0, sizeof(*port));
278 port->id = i; 278 port->id = i;
279 INIT_LIST_HEAD(&port->dev_list); 279 INIT_LIST_HEAD(&port->dev_list);
280 INIT_LIST_HEAD(&port->disco_list);
281 INIT_LIST_HEAD(&port->destroy_list);
280 spin_lock_init(&port->phy_list_lock); 282 spin_lock_init(&port->phy_list_lock);
281 INIT_LIST_HEAD(&port->phy_list); 283 INIT_LIST_HEAD(&port->phy_list);
282 port->ha = sas_ha; 284 port->ha = sas_ha;
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 9d9330ae4213..9421bae8af1a 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -1603,6 +1603,20 @@ sas_rphy_delete(struct sas_rphy *rphy)
1603EXPORT_SYMBOL(sas_rphy_delete); 1603EXPORT_SYMBOL(sas_rphy_delete);
1604 1604
1605/** 1605/**
1606 * sas_rphy_unlink - unlink SAS remote PHY
1607 * @rphy: SAS remote phy to unlink from its parent port
1608 *
1609 * Removes port reference to an rphy
1610 */
1611void sas_rphy_unlink(struct sas_rphy *rphy)
1612{
1613 struct sas_port *parent = dev_to_sas_port(rphy->dev.parent);
1614
1615 parent->rphy = NULL;
1616}
1617EXPORT_SYMBOL(sas_rphy_unlink);
1618
1619/**
1606 * sas_rphy_remove - remove SAS remote PHY 1620 * sas_rphy_remove - remove SAS remote PHY
1607 * @rphy: SAS remote phy to remove 1621 * @rphy: SAS remote phy to remove
1608 * 1622 *
@@ -1612,7 +1626,6 @@ void
1612sas_rphy_remove(struct sas_rphy *rphy) 1626sas_rphy_remove(struct sas_rphy *rphy)
1613{ 1627{
1614 struct device *dev = &rphy->dev; 1628 struct device *dev = &rphy->dev;
1615 struct sas_port *parent = dev_to_sas_port(dev->parent);
1616 1629
1617 switch (rphy->identify.device_type) { 1630 switch (rphy->identify.device_type) {
1618 case SAS_END_DEVICE: 1631 case SAS_END_DEVICE:
@@ -1626,10 +1639,9 @@ sas_rphy_remove(struct sas_rphy *rphy)
1626 break; 1639 break;
1627 } 1640 }
1628 1641
1642 sas_rphy_unlink(rphy);
1629 transport_remove_device(dev); 1643 transport_remove_device(dev);
1630 device_del(dev); 1644 device_del(dev);
1631
1632 parent->rphy = NULL;
1633} 1645}
1634EXPORT_SYMBOL(sas_rphy_remove); 1646EXPORT_SYMBOL(sas_rphy_remove);
1635 1647