summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas/sas_init.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-12-02 16:07:01 -0800
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 14:13:51 -0600
commit81c757bc696284f39f07766f0c2ca67af64ce9bd (patch)
treea01a13219199520c7a992f90d54cc83513a5cdb0 /drivers/scsi/libsas/sas_init.c
parent0b3e09da1350397f3f8b6fd839ab455b0b587451 (diff)
downloadlinux-81c757bc696284f39f07766f0c2ca67af64ce9bd.tar.bz2
[SCSI] libsas: execute transport link resets with libata-eh via host workqueue
Link resets leave ata affiliations intact, so arrange for libsas to make an effort to avoid dropping the device due to a slow-to-recover link. Towards this end carry out reset in the host workqueue so that it can check for ata devices and kick the reset request to libata. Hard resets, in contrast, bypass libata since they are meant for associating an ata device with another initiator in the domain (tears down affiliations). Need to add a new transport_sas_phy_reset() since the current sas_phy_reset() is a utility function to libsas lldds. They are not prepared for it to loop back into eh. 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_init.c')
-rw-r--r--drivers/scsi/libsas/sas_init.c56
1 files changed, 55 insertions, 1 deletions
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index cb65adf4ab16..a15fb861daba 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -28,6 +28,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <scsi/sas_ata.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
@@ -195,6 +196,59 @@ static int sas_get_linkerrors(struct sas_phy *phy)
return sas_smp_get_phy_events(phy);
}
+/**
+ * transport_sas_phy_reset - reset a phy and permit libata to manage the link
+ *
+ * phy reset request via sysfs in host workqueue context so we know we
+ * can block on eh and safely traverse the domain_device topology
+ */
+static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ int ret;
+ enum phy_func reset_type;
+
+ if (hard_reset)
+ reset_type = PHY_FUNC_HARD_RESET;
+ else
+ reset_type = PHY_FUNC_LINK_RESET;
+
+ if (scsi_is_sas_phy_local(phy)) {
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->core.shost->transportt);
+ struct domain_device *dev = NULL;
+
+ if (asd_phy->port)
+ dev = asd_phy->port->port_dev;
+
+ /* validate that dev has been probed */
+ if (dev)
+ dev = sas_find_dev_by_rphy(dev->rphy);
+
+ if (dev && dev_is_sata(dev) && !hard_reset) {
+ sas_ata_schedule_reset(dev);
+ sas_ata_wait_eh(dev);
+ ret = 0;
+ } else
+ ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
+ } else {
+ struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
+ struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+ struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
+
+ if (ata_dev && !hard_reset) {
+ sas_ata_schedule_reset(ata_dev);
+ sas_ata_wait_eh(ata_dev);
+ ret = 0;
+ } else
+ ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
+ }
+
+ return ret;
+}
+
int sas_phy_enable(struct sas_phy *phy, int enable)
{
int ret;
@@ -300,7 +354,7 @@ static void phy_reset_work(struct work_struct *work)
{
struct sas_phy_data *d = container_of(work, typeof(*d), reset_work);
- d->reset_result = sas_phy_reset(d->phy, d->hard_reset);
+ d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
}
static int sas_phy_setup(struct sas_phy *phy)