diff options
author | Darrick J. Wong <djwong@us.ibm.com> | 2006-11-07 20:28:55 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-11-22 12:05:59 -0500 |
commit | dea22214790d1306f3a3444db13d2c726037b189 (patch) | |
tree | c5bd0d382c73c02f3416aac584e3d3a011cd1261 /drivers/scsi | |
parent | 504fb37a0801d843bc1907c1a1f9c719c3509863 (diff) |
[PATCH] aic94xx: handle REQ_DEVICE_RESET
This patch implements a REQ_DEVICE_RESET handler for the aic94xx
driver. Like the earlier REQ_TASK_ABORT patch, this patch defers the
device reset to the Scsi_Host's workqueue, which has the added benefit
of ensuring that the device reset does not happen at the same time
that the abort tmfs are being processed. After the phy reset, the
busted drive should go away and be re-detected later, which is indeed
what I've seen on both a x260 and a x206m.
Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 51 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 2 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 1 |
3 files changed, 46 insertions, 8 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 1911c5d17875..a014418d670e 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c | |||
@@ -343,6 +343,27 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) | |||
343 | } | 343 | } |
344 | } | 344 | } |
345 | 345 | ||
346 | /* hard reset a phy later */ | ||
347 | static void do_phy_reset_later(void *data) | ||
348 | { | ||
349 | struct sas_phy *sas_phy = data; | ||
350 | int error; | ||
351 | |||
352 | ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__, | ||
353 | sas_phy->identify.phy_identifier); | ||
354 | /* Reset device port */ | ||
355 | error = sas_phy_reset(sas_phy, 1); | ||
356 | if (error) | ||
357 | ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n", | ||
358 | __FUNCTION__, sas_phy->identify.phy_identifier, error); | ||
359 | } | ||
360 | |||
361 | static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost) | ||
362 | { | ||
363 | INIT_WORK(&sas_phy->reset_work, do_phy_reset_later, sas_phy); | ||
364 | queue_work(shost->work_q, &sas_phy->reset_work); | ||
365 | } | ||
366 | |||
346 | /* start up the ABORT TASK tmf... */ | 367 | /* start up the ABORT TASK tmf... */ |
347 | static void task_kill_later(struct asd_ascb *ascb) | 368 | static void task_kill_later(struct asd_ascb *ascb) |
348 | { | 369 | { |
@@ -402,7 +423,9 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
402 | goto out; | 423 | goto out; |
403 | } | 424 | } |
404 | case REQ_DEVICE_RESET: { | 425 | case REQ_DEVICE_RESET: { |
405 | struct asd_ascb *a, *b; | 426 | struct Scsi_Host *shost = sas_ha->core.shost; |
427 | struct sas_phy *dev_phy; | ||
428 | struct asd_ascb *a; | ||
406 | u16 conn_handle; | 429 | u16 conn_handle; |
407 | 430 | ||
408 | conn_handle = *((u16*)(&dl->status_block[1])); | 431 | conn_handle = *((u16*)(&dl->status_block[1])); |
@@ -412,17 +435,31 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, | |||
412 | dl->status_block[3]); | 435 | dl->status_block[3]); |
413 | 436 | ||
414 | /* Kill all pending tasks and reset the device */ | 437 | /* Kill all pending tasks and reset the device */ |
415 | list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { | 438 | dev_phy = NULL; |
416 | struct sas_task *task = a->uldd_task; | 439 | list_for_each_entry(a, &asd_ha->seq.pend_q, list) { |
417 | struct domain_device *dev = task->dev; | 440 | struct sas_task *task; |
441 | struct domain_device *dev; | ||
418 | u16 x; | 442 | u16 x; |
419 | 443 | ||
420 | x = *((u16*)(&dev->lldd_dev)); | 444 | task = a->uldd_task; |
421 | if (x == conn_handle) | 445 | if (!task) |
446 | continue; | ||
447 | dev = task->dev; | ||
448 | |||
449 | x = (u16)dev->lldd_dev; | ||
450 | if (x == conn_handle) { | ||
451 | dev_phy = dev->port->phy; | ||
422 | task_kill_later(a); | 452 | task_kill_later(a); |
453 | } | ||
423 | } | 454 | } |
424 | 455 | ||
425 | /* FIXME: Reset device port (huh?) */ | 456 | /* Reset device port */ |
457 | if (!dev_phy) { | ||
458 | ASD_DPRINTK("%s: No pending commands; can't reset.\n", | ||
459 | __FUNCTION__); | ||
460 | goto out; | ||
461 | } | ||
462 | phy_reset_later(dev_phy, shost); | ||
426 | goto out; | 463 | goto out; |
427 | } | 464 | } |
428 | case SIGNAL_NCQ_ERROR: | 465 | case SIGNAL_NCQ_ERROR: |
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index a2b479a65908..0fb347b4b1a2 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -144,7 +144,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) | |||
144 | return sas_smp_get_phy_events(phy); | 144 | return sas_smp_get_phy_events(phy); |
145 | } | 145 | } |
146 | 146 | ||
147 | static int sas_phy_reset(struct sas_phy *phy, int hard_reset) | 147 | int sas_phy_reset(struct sas_phy *phy, int hard_reset) |
148 | { | 148 | { |
149 | int ret; | 149 | int ret; |
150 | enum phy_func reset_type; | 150 | enum phy_func reset_type; |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c5fd37522728..e064aac06b90 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
@@ -865,3 +865,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_depth); | |||
865 | EXPORT_SYMBOL_GPL(sas_change_queue_type); | 865 | EXPORT_SYMBOL_GPL(sas_change_queue_type); |
866 | EXPORT_SYMBOL_GPL(sas_bios_param); | 866 | EXPORT_SYMBOL_GPL(sas_bios_param); |
867 | EXPORT_SYMBOL_GPL(sas_task_abort); | 867 | EXPORT_SYMBOL_GPL(sas_task_abort); |
868 | EXPORT_SYMBOL_GPL(sas_phy_reset); | ||