aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/scsi/libsas/sas_ata.c19
-rw-r--r--drivers/scsi/libsas/sas_expander.c44
-rw-r--r--include/scsi/sas_ata.h6
3 files changed, 63 insertions, 6 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index a8ace8d24e66..48cadf88c399 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
return rtn;
}
+
+void sas_ata_schedule_reset(struct domain_device *dev)
+{
+ struct ata_eh_info *ehi;
+ struct ata_port *ap;
+ unsigned long flags;
+
+ if (!dev_is_sata(dev))
+ return;
+
+ ap = dev->sata_dev.ap;
+ ehi = &ap->link.eh_info;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ehi->err_mask |= AC_ERR_TIMEOUT;
+ ehi->action |= ATA_EH_RESET;
+ ata_port_schedule_eh(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index e45b259dac4c..f4894b0f537b 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -28,6 +28,7 @@
#include "sas_internal.h"
+#include <scsi/sas_ata.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
#include "../scsi_sas_internal.h"
@@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
return;
}
+/* check if we have an existing attached ata device on this expander phy */
+static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
+{
+ struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
+ struct domain_device *dev;
+ struct sas_rphy *rphy;
+
+ if (!ex_phy->port)
+ return NULL;
+
+ rphy = ex_phy->port->rphy;
+ if (!rphy)
+ return NULL;
+
+ dev = sas_find_dev_by_rphy(rphy);
+
+ if (dev && dev_is_sata(dev))
+ return dev;
+
+ return NULL;
+}
+
#define DISCOVER_REQ_SIZE 16
#define DISCOVER_RESP_SIZE 56
static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
u8 *disc_resp, int single)
{
+ struct domain_device *ata_dev = sas_ex_to_ata(dev, single);
int i, res;
disc_req[9] = single;
@@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
disc_resp, DISCOVER_RESP_SIZE);
if (res)
return res;
- /* This is detecting a failure to transmit initial
- * dev to host FIS as described in section G.5 of
- * sas-2 r 04b */
dr = &((struct smp_resp *)disc_resp)->disc;
if (memcmp(dev->sas_addr, dr->attached_sas_addr,
SAS_ADDR_SIZE) == 0) {
sas_printk("Found loopback topology, just ignore it!\n");
return 0;
}
+
+ /* This is detecting a failure to transmit initial
+ * dev to host FIS as described in section J.5 of
+ * sas-2 r16
+ */
if (!(dr->attached_dev_type == 0 &&
dr->attached_sata_dev))
break;
- /* In order to generate the dev to host FIS, we
- * send a link reset to the expander port */
+
+ /* In order to generate the dev to host FIS, we send a
+ * link reset to the expander port. If a device was
+ * previously detected on this port we ask libata to
+ * manage the reset and link recovery.
+ */
+ if (ata_dev) {
+ sas_ata_schedule_reset(ata_dev);
+ break;
+ }
sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
/* Wait for the reset to trigger the negotiation */
msleep(500);
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h
index 9f7a23d1146d..c0bcd30eec56 100644
--- a/include/scsi/sas_ata.h
+++ b/include/scsi/sas_ata.h
@@ -44,7 +44,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost);
int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
struct list_head *done_q);
void sas_probe_sata(struct work_struct *work);
-
+void sas_ata_schedule_reset(struct domain_device *dev);
#else
@@ -75,6 +75,10 @@ static inline void sas_probe_sata(struct work_struct *work)
{
}
+static inline void sas_ata_schedule_reset(struct domain_device *dev)
+{
+}
+
#endif
#endif /* _SAS_ATA_H_ */