Commit b52df417 authored by Dan Williams's avatar Dan Williams Committed by James Bottomley

[SCSI] libsas: use libata-eh-reset for sata rediscovery fis transmit failures

Since sata devices can take several seconds to recover the link on reset
the 0.5 seconds that libsas currently waits may not be enough.  Instead
if we are rediscovering a phy that was previously attached to a sata
device let libata handle any resets to encourage the device to transmit
the initial fis.

Once sas_ata_hard_reset() and lldds learn how to honor 'deadline' libsas
should stop encountering phys in an intermediate state, until then this
will loop until the fis is transmitted or ->attached_sas_addr gets
cleared, but in the more likely initial discovery case we keep existing
behavior.
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 3a2cdf39
...@@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, ...@@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
return rtn; 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);
}
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "sas_internal.h" #include "sas_internal.h"
#include <scsi/sas_ata.h>
#include <scsi/scsi_transport.h> #include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h> #include <scsi/scsi_transport_sas.h>
#include "../scsi_sas_internal.h" #include "../scsi_sas_internal.h"
...@@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, ...@@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
return; 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_REQ_SIZE 16
#define DISCOVER_RESP_SIZE 56 #define DISCOVER_RESP_SIZE 56
static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
u8 *disc_resp, int single) u8 *disc_resp, int single)
{ {
struct domain_device *ata_dev = sas_ex_to_ata(dev, single);
int i, res; int i, res;
disc_req[9] = single; disc_req[9] = single;
...@@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, ...@@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
disc_resp, DISCOVER_RESP_SIZE); disc_resp, DISCOVER_RESP_SIZE);
if (res) if (res)
return 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; dr = &((struct smp_resp *)disc_resp)->disc;
if (memcmp(dev->sas_addr, dr->attached_sas_addr, if (memcmp(dev->sas_addr, dr->attached_sas_addr,
SAS_ADDR_SIZE) == 0) { SAS_ADDR_SIZE) == 0) {
sas_printk("Found loopback topology, just ignore it!\n"); sas_printk("Found loopback topology, just ignore it!\n");
return 0; 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 && if (!(dr->attached_dev_type == 0 &&
dr->attached_sata_dev)) dr->attached_sata_dev))
break; 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); sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
/* Wait for the reset to trigger the negotiation */ /* Wait for the reset to trigger the negotiation */
msleep(500); msleep(500);
......
...@@ -44,7 +44,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost); ...@@ -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, int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
struct list_head *done_q); struct list_head *done_q);
void sas_probe_sata(struct work_struct *work); void sas_probe_sata(struct work_struct *work);
void sas_ata_schedule_reset(struct domain_device *dev);
#else #else
...@@ -75,6 +75,10 @@ static inline void sas_probe_sata(struct work_struct *work) ...@@ -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
#endif /* _SAS_ATA_H_ */ #endif /* _SAS_ATA_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment