summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas/sas_ata.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-11-17 17:59:51 -0800
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 13:52:34 -0600
commit87c8331fcf72e501c3a3c0cdc5c9391ec72f7cf2 (patch)
tree4ed0e98760c977010fe54778c1a25625840b4583 /drivers/scsi/libsas/sas_ata.c
parente139942d77a6e3ac83bc322e826668054a8601d6 (diff)
downloadlinux-87c8331fcf72e501c3a3c0cdc5c9391ec72f7cf2.tar.bz2
[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/libsas/sas_ata.c')
-rw-r--r--drivers/scsi/libsas/sas_ata.c55
1 files changed, 51 insertions, 4 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)
return -ENODEV;
}
+void sas_probe_sata(struct work_struct *work)
+{
+ struct domain_device *dev, *n;
+ struct sas_discovery_event *ev =
+ container_of(work, struct sas_discovery_event, work);
+ struct asd_sas_port *port = ev->port;
+
+ clear_bit(DISCE_PROBE, &port->disc.pending);
+
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ int err;
+
+ spin_lock_irq(&port->dev_list_lock);
+ list_add_tail(&dev->dev_list_node, &port->dev_list);
+ spin_unlock_irq(&port->dev_list_lock);
+
+ err = sas_rphy_add(dev->rphy);
+
+ if (err) {
+ SAS_DPRINTK("%s: for %s device %16llx returned %d\n",
+ __func__, dev->parent ? "exp-attached" :
+ "direct-attached",
+ SAS_ADDR(dev->sas_addr), err);
+ sas_unregister_dev(port, dev);
+ } else
+ list_del_init(&dev->disco_list_node);
+ }
+}
+
/**
* sas_discover_sata -- discover an STP/SATA domain device
* @dev: pointer to struct domain_device of interest
@@ -794,10 +823,15 @@ int sas_discover_sata(struct domain_device *dev)
break;
}
sas_notify_lldd_dev_gone(dev);
- if (!res) {
- sas_notify_lldd_dev_found(dev);
- res = sas_rphy_add(dev->rphy);
- }
+
+ if (res)
+ return res;
+
+ res = sas_notify_lldd_dev_found(dev);
+ if (res)
+ return res;
+
+ sas_discover_event(dev->port, DISCE_PROBE);
return res;
}
@@ -805,6 +839,17 @@ int sas_discover_sata(struct domain_device *dev)
void sas_ata_strategy_handler(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+
+ /* it's ok to defer revalidation events during ata eh, these
+ * disks are in one of three states:
+ * 1/ present for initial domain discovery, and these
+ * resets will cause bcn flutters
+ * 2/ hot removed, we'll discover that after eh fails
+ * 3/ hot added after initial discovery, lost the race, and need
+ * to catch the next train.
+ */
+ sas_disable_revalidation(sas_ha);
shost_for_each_device(sdev, shost) {
struct domain_device *ddev = sdev_to_domain_dev(sdev);
@@ -816,6 +861,8 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler");
ata_scsi_port_error_handler(shost, ap);
}
+
+ sas_enable_revalidation(sas_ha);
}
int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,