[SCSI] libsas: poll for ata device readiness after reset
Use ata_wait_after_reset() to poll for link recovery after a reset. This combined with sas_ha->eh_mutex prevents expander rediscovery from probing phys in an intermediate state. Local discovery does not have a mechanism to filter link status changes during this timeout, so it remains the responsibility of lldds to prevent premature port teardown. Although once all lldd's support ->lldd_ata_check_ready() that could be used as a gate to local port teardown. The signature fis is re-transmitted when the link comes back so we should be revalidating the ata device class, but that is left to a future patch. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
parent
50824d6c56
commit
36a3994739
|
@ -272,39 +272,84 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||
return true;
|
||||
}
|
||||
|
||||
static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
|
||||
unsigned long deadline)
|
||||
static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
|
||||
{
|
||||
return to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
}
|
||||
|
||||
static int smp_ata_check_ready(struct ata_link *link)
|
||||
{
|
||||
int res;
|
||||
u8 addr[8];
|
||||
struct ata_port *ap = link->ap;
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct domain_device *ex_dev = dev->parent;
|
||||
struct sas_phy *phy = sas_find_local_phy(dev);
|
||||
|
||||
res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr);
|
||||
/* break the wait early if the expander is unreachable,
|
||||
* otherwise keep polling
|
||||
*/
|
||||
if (res == -ECOMM)
|
||||
return res;
|
||||
if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int local_ata_check_ready(struct ata_link *link)
|
||||
{
|
||||
struct ata_port *ap = link->ap;
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
int ret = 0;
|
||||
struct sas_internal *i = dev_to_sas_internal(dev);
|
||||
|
||||
if (i->dft->lldd_I_T_nexus_reset)
|
||||
res = i->dft->lldd_I_T_nexus_reset(dev);
|
||||
|
||||
if (res != TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__);
|
||||
ret = -EAGAIN;
|
||||
if (i->dft->lldd_ata_check_ready)
|
||||
return i->dft->lldd_ata_check_ready(dev);
|
||||
else {
|
||||
/* lldd's that don't implement 'ready' checking get the
|
||||
* old default behavior of not coordinating reset
|
||||
* recovery with libata
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
|
||||
unsigned long deadline)
|
||||
{
|
||||
int ret = 0, res;
|
||||
struct ata_port *ap = link->ap;
|
||||
int (*check_ready)(struct ata_link *link);
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_phy *phy = sas_find_local_phy(dev);
|
||||
struct sas_internal *i = dev_to_sas_internal(dev);
|
||||
|
||||
res = i->dft->lldd_I_T_nexus_reset(dev);
|
||||
|
||||
if (res != TMF_RESP_FUNC_COMPLETE)
|
||||
SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__);
|
||||
|
||||
if (scsi_is_sas_phy_local(phy))
|
||||
check_ready = local_ata_check_ready;
|
||||
else
|
||||
check_ready = smp_ata_check_ready;
|
||||
|
||||
ret = ata_wait_after_reset(link, deadline, check_ready);
|
||||
if (ret && ret != -EAGAIN)
|
||||
ata_link_err(link, "COMRESET failed (errno=%d)\n", ret);
|
||||
|
||||
/* XXX: if the class changes during the reset the upper layer
|
||||
* should be informed, if the device has gone away we assume
|
||||
* libsas will eventually delete it
|
||||
*/
|
||||
switch (dev->sata_dev.command_set) {
|
||||
case ATA_COMMAND_SET:
|
||||
SAS_DPRINTK("%s: Found ATA device.\n", __func__);
|
||||
*class = ATA_DEV_ATA;
|
||||
break;
|
||||
case ATAPI_COMMAND_SET:
|
||||
SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
|
||||
*class = ATA_DEV_ATAPI;
|
||||
break;
|
||||
default:
|
||||
SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
|
||||
__func__,
|
||||
dev->sata_dev.command_set);
|
||||
*class = ATA_DEV_UNKNOWN;
|
||||
break;
|
||||
case ATA_COMMAND_SET:
|
||||
*class = ATA_DEV_ATA;
|
||||
break;
|
||||
case ATAPI_COMMAND_SET:
|
||||
*class = ATA_DEV_ATAPI;
|
||||
break;
|
||||
}
|
||||
|
||||
ap->cbl = ATA_CBL_SATA;
|
||||
|
@ -316,8 +361,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class,
|
|||
{
|
||||
struct ata_port *ap = link->ap;
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
struct sas_internal *i = dev_to_sas_internal(dev);
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -355,8 +399,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class,
|
|||
*/
|
||||
static void sas_ata_internal_abort(struct sas_task *task)
|
||||
{
|
||||
struct sas_internal *si =
|
||||
to_sas_internal(task->dev->port->ha->core.shost->transportt);
|
||||
struct sas_internal *si = dev_to_sas_internal(task->dev);
|
||||
unsigned long flags;
|
||||
int res;
|
||||
|
||||
|
@ -425,8 +468,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc)
|
|||
static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)
|
||||
{
|
||||
struct domain_device *dev = ap->private_data;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
struct sas_internal *i = dev_to_sas_internal(dev);
|
||||
|
||||
if (i->dft->lldd_ata_set_dmamode)
|
||||
i->dft->lldd_ata_set_dmamode(dev);
|
||||
|
|
|
@ -125,7 +125,11 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
|
|||
task->task_status.stat == SAS_DATA_OVERRUN) {
|
||||
res = -EMSGSIZE;
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
if (task->task_status.resp == SAS_TASK_UNDELIVERED &&
|
||||
task->task_status.stat == SAS_DEVICE_UNKNOWN)
|
||||
break;
|
||||
else {
|
||||
SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "
|
||||
"status 0x%x\n", __func__,
|
||||
SAS_ADDR(dev->sas_addr),
|
||||
|
@ -1648,8 +1652,8 @@ static int sas_get_phy_change_count(struct domain_device *dev,
|
|||
return res;
|
||||
}
|
||||
|
||||
static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
|
||||
int phy_id, u8 *attached_sas_addr)
|
||||
int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id,
|
||||
u8 *attached_sas_addr)
|
||||
{
|
||||
int res;
|
||||
struct smp_resp *disc_resp;
|
||||
|
|
|
@ -89,7 +89,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy);
|
|||
|
||||
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
|
||||
struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id);
|
||||
|
||||
int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id,
|
||||
u8 *attached_sas_addr);
|
||||
void sas_hae_reset(struct work_struct *work);
|
||||
|
||||
void sas_free_device(struct kref *kref);
|
||||
|
|
|
@ -615,6 +615,7 @@ struct sas_domain_function_template {
|
|||
int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_I_T_nexus_reset)(struct domain_device *);
|
||||
int (*lldd_ata_soft_reset)(struct domain_device *);
|
||||
int (*lldd_ata_check_ready)(struct domain_device *);
|
||||
void (*lldd_ata_set_dmamode)(struct domain_device *);
|
||||
int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
|
||||
int (*lldd_query_task)(struct sas_task *);
|
||||
|
|
Loading…
Reference in New Issue