aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic94xx
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@us.ibm.com>2006-11-07 20:28:55 -0500
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-11-22 12:05:59 -0500
commitdea22214790d1306f3a3444db13d2c726037b189 (patch)
treec5bd0d382c73c02f3416aac584e3d3a011cd1261 /drivers/scsi/aic94xx
parent504fb37a0801d843bc1907c1a1f9c719c3509863 (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/aic94xx')
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c51
1 files changed, 44 insertions, 7 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 */
347static 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
361static 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... */
347static void task_kill_later(struct asd_ascb *ascb) 368static 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: