diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-11-17 20:59:54 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-29 13:49:36 -0500 |
commit | 36a399473902a57218dc493c5a814708a56b73ab (patch) | |
tree | e64a9f136bbacaded9fdd3d3d39c342953d9be34 /drivers/scsi/libsas | |
parent | 50824d6c5657ce340e3911171865a8d99fdd8eba (diff) |
[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>
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 104 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 10 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 3 |
3 files changed, 82 insertions, 35 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4beca66728b4..5fdb63ad94b7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c | |||
@@ -272,39 +272,84 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) | |||
272 | return true; | 272 | return true; |
273 | } | 273 | } |
274 | 274 | ||
275 | static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, | 275 | static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) |
276 | unsigned long deadline) | 276 | { |
277 | return to_sas_internal(dev->port->ha->core.shost->transportt); | ||
278 | } | ||
279 | |||
280 | static int smp_ata_check_ready(struct ata_link *link) | ||
277 | { | 281 | { |
282 | int res; | ||
283 | u8 addr[8]; | ||
278 | struct ata_port *ap = link->ap; | 284 | struct ata_port *ap = link->ap; |
279 | struct domain_device *dev = ap->private_data; | 285 | struct domain_device *dev = ap->private_data; |
280 | struct sas_internal *i = | 286 | struct domain_device *ex_dev = dev->parent; |
281 | to_sas_internal(dev->port->ha->core.shost->transportt); | 287 | struct sas_phy *phy = sas_find_local_phy(dev); |
282 | int res = TMF_RESP_FUNC_FAILED; | ||
283 | int ret = 0; | ||
284 | 288 | ||
285 | if (i->dft->lldd_I_T_nexus_reset) | 289 | res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); |
286 | res = i->dft->lldd_I_T_nexus_reset(dev); | 290 | /* break the wait early if the expander is unreachable, |
291 | * otherwise keep polling | ||
292 | */ | ||
293 | if (res == -ECOMM) | ||
294 | return res; | ||
295 | if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0) | ||
296 | return 0; | ||
297 | else | ||
298 | return 1; | ||
299 | } | ||
287 | 300 | ||
288 | if (res != TMF_RESP_FUNC_COMPLETE) { | 301 | static int local_ata_check_ready(struct ata_link *link) |
289 | SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); | 302 | { |
290 | ret = -EAGAIN; | 303 | struct ata_port *ap = link->ap; |
304 | struct domain_device *dev = ap->private_data; | ||
305 | struct sas_internal *i = dev_to_sas_internal(dev); | ||
306 | |||
307 | if (i->dft->lldd_ata_check_ready) | ||
308 | return i->dft->lldd_ata_check_ready(dev); | ||
309 | else { | ||
310 | /* lldd's that don't implement 'ready' checking get the | ||
311 | * old default behavior of not coordinating reset | ||
312 | * recovery with libata | ||
313 | */ | ||
314 | return 1; | ||
291 | } | 315 | } |
316 | } | ||
292 | 317 | ||
318 | static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, | ||
319 | unsigned long deadline) | ||
320 | { | ||
321 | int ret = 0, res; | ||
322 | struct ata_port *ap = link->ap; | ||
323 | int (*check_ready)(struct ata_link *link); | ||
324 | struct domain_device *dev = ap->private_data; | ||
325 | struct sas_phy *phy = sas_find_local_phy(dev); | ||
326 | struct sas_internal *i = dev_to_sas_internal(dev); | ||
327 | |||
328 | res = i->dft->lldd_I_T_nexus_reset(dev); | ||
329 | |||
330 | if (res != TMF_RESP_FUNC_COMPLETE) | ||
331 | SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); | ||
332 | |||
333 | if (scsi_is_sas_phy_local(phy)) | ||
334 | check_ready = local_ata_check_ready; | ||
335 | else | ||
336 | check_ready = smp_ata_check_ready; | ||
337 | |||
338 | ret = ata_wait_after_reset(link, deadline, check_ready); | ||
339 | if (ret && ret != -EAGAIN) | ||
340 | ata_link_err(link, "COMRESET failed (errno=%d)\n", ret); | ||
341 | |||
342 | /* XXX: if the class changes during the reset the upper layer | ||
343 | * should be informed, if the device has gone away we assume | ||
344 | * libsas will eventually delete it | ||
345 | */ | ||
293 | switch (dev->sata_dev.command_set) { | 346 | switch (dev->sata_dev.command_set) { |
294 | case ATA_COMMAND_SET: | 347 | case ATA_COMMAND_SET: |
295 | SAS_DPRINTK("%s: Found ATA device.\n", __func__); | 348 | *class = ATA_DEV_ATA; |
296 | *class = ATA_DEV_ATA; | 349 | break; |
297 | break; | 350 | case ATAPI_COMMAND_SET: |
298 | case ATAPI_COMMAND_SET: | 351 | *class = ATA_DEV_ATAPI; |
299 | SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); | 352 | break; |
300 | *class = ATA_DEV_ATAPI; | ||
301 | break; | ||
302 | default: | ||
303 | SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", | ||
304 | __func__, | ||
305 | dev->sata_dev.command_set); | ||
306 | *class = ATA_DEV_UNKNOWN; | ||
307 | break; | ||
308 | } | 353 | } |
309 | 354 | ||
310 | ap->cbl = ATA_CBL_SATA; | 355 | ap->cbl = ATA_CBL_SATA; |
@@ -316,8 +361,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, | |||
316 | { | 361 | { |
317 | struct ata_port *ap = link->ap; | 362 | struct ata_port *ap = link->ap; |
318 | struct domain_device *dev = ap->private_data; | 363 | struct domain_device *dev = ap->private_data; |
319 | struct sas_internal *i = | 364 | struct sas_internal *i = dev_to_sas_internal(dev); |
320 | to_sas_internal(dev->port->ha->core.shost->transportt); | ||
321 | int res = TMF_RESP_FUNC_FAILED; | 365 | int res = TMF_RESP_FUNC_FAILED; |
322 | int ret = 0; | 366 | int ret = 0; |
323 | 367 | ||
@@ -355,8 +399,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, | |||
355 | */ | 399 | */ |
356 | static void sas_ata_internal_abort(struct sas_task *task) | 400 | static void sas_ata_internal_abort(struct sas_task *task) |
357 | { | 401 | { |
358 | struct sas_internal *si = | 402 | struct sas_internal *si = dev_to_sas_internal(task->dev); |
359 | to_sas_internal(task->dev->port->ha->core.shost->transportt); | ||
360 | unsigned long flags; | 403 | unsigned long flags; |
361 | int res; | 404 | int res; |
362 | 405 | ||
@@ -425,8 +468,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) | |||
425 | static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) | 468 | static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) |
426 | { | 469 | { |
427 | struct domain_device *dev = ap->private_data; | 470 | struct domain_device *dev = ap->private_data; |
428 | struct sas_internal *i = | 471 | struct sas_internal *i = dev_to_sas_internal(dev); |
429 | to_sas_internal(dev->port->ha->core.shost->transportt); | ||
430 | 472 | ||
431 | if (i->dft->lldd_ata_set_dmamode) | 473 | if (i->dft->lldd_ata_set_dmamode) |
432 | i->dft->lldd_ata_set_dmamode(dev); | 474 | i->dft->lldd_ata_set_dmamode(dev); |
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 7c59f97c0287..32e417e6c2f7 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c | |||
@@ -125,7 +125,11 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, | |||
125 | task->task_status.stat == SAS_DATA_OVERRUN) { | 125 | task->task_status.stat == SAS_DATA_OVERRUN) { |
126 | res = -EMSGSIZE; | 126 | res = -EMSGSIZE; |
127 | break; | 127 | break; |
128 | } else { | 128 | } |
129 | if (task->task_status.resp == SAS_TASK_UNDELIVERED && | ||
130 | task->task_status.stat == SAS_DEVICE_UNKNOWN) | ||
131 | break; | ||
132 | else { | ||
129 | SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " | 133 | SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " |
130 | "status 0x%x\n", __func__, | 134 | "status 0x%x\n", __func__, |
131 | SAS_ADDR(dev->sas_addr), | 135 | SAS_ADDR(dev->sas_addr), |
@@ -1648,8 +1652,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, | |||
1648 | return res; | 1652 | return res; |
1649 | } | 1653 | } |
1650 | 1654 | ||
1651 | static int sas_get_phy_attached_sas_addr(struct domain_device *dev, | 1655 | int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, |
1652 | int phy_id, u8 *attached_sas_addr) | 1656 | u8 *attached_sas_addr) |
1653 | { | 1657 | { |
1654 | int res; | 1658 | int res; |
1655 | struct smp_resp *disc_resp; | 1659 | struct smp_resp *disc_resp; |
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9e960b2d535a..a9a3bb94c1bc 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h | |||
@@ -89,7 +89,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy); | |||
89 | 89 | ||
90 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); | 90 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); |
91 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); | 91 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); |
92 | 92 | int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, | |
93 | u8 *attached_sas_addr); | ||
93 | void sas_hae_reset(struct work_struct *work); | 94 | void sas_hae_reset(struct work_struct *work); |
94 | 95 | ||
95 | void sas_free_device(struct kref *kref); | 96 | void sas_free_device(struct kref *kref); |