aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic94xx/aic94xx_scb.c
diff options
context:
space:
mode:
authorJeff Garzik <jeff@garzik.org>2007-02-17 15:09:59 -0500
committerJeff Garzik <jeff@garzik.org>2007-02-17 15:09:59 -0500
commit48c871c1f6a7c7044dd76774fb469e65c7e2e4e8 (patch)
treeda3aa535c98cc0957851354ceb0fbff7482d7a9d /drivers/scsi/aic94xx/aic94xx_scb.c
parent1a1689344add3333d28d1b5495d8043a3877d01c (diff)
parent4409d28140d9a6e6e3f4f1fdaf7234c4b965d954 (diff)
Merge branch 'gfar' of master.kernel.org:/pub/scm/linux/kernel/git/galak/powerpc into upstream
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_scb.c')
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c122
1 files changed, 66 insertions, 56 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 75ed6b0569d1..8f43ff772f23 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -413,40 +413,6 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
413 } 413 }
414} 414}
415 415
416/* hard reset a phy later */
417static void do_phy_reset_later(struct work_struct *work)
418{
419 struct sas_phy *sas_phy =
420 container_of(work, struct sas_phy, reset_work);
421 int error;
422
423 ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__,
424 sas_phy->identify.phy_identifier);
425 /* Reset device port */
426 error = sas_phy_reset(sas_phy, 1);
427 if (error)
428 ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n",
429 __FUNCTION__, sas_phy->identify.phy_identifier, error);
430}
431
432static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost)
433{
434 INIT_WORK(&sas_phy->reset_work, do_phy_reset_later);
435 queue_work(shost->work_q, &sas_phy->reset_work);
436}
437
438/* start up the ABORT TASK tmf... */
439static void task_kill_later(struct asd_ascb *ascb)
440{
441 struct asd_ha_struct *asd_ha = ascb->ha;
442 struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
443 struct Scsi_Host *shost = sas_ha->core.shost;
444 struct sas_task *task = ascb->uldd_task;
445
446 INIT_WORK(&task->abort_work, sas_task_abort);
447 queue_work(shost->work_q, &task->abort_work);
448}
449
450static void escb_tasklet_complete(struct asd_ascb *ascb, 416static void escb_tasklet_complete(struct asd_ascb *ascb,
451 struct done_list_struct *dl) 417 struct done_list_struct *dl)
452{ 418{
@@ -479,26 +445,55 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
479 case REQ_TASK_ABORT: { 445 case REQ_TASK_ABORT: {
480 struct asd_ascb *a, *b; 446 struct asd_ascb *a, *b;
481 u16 tc_abort; 447 u16 tc_abort;
448 struct domain_device *failed_dev = NULL;
449
450 ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
451 __FUNCTION__, dl->status_block[3]);
482 452
453 /*
454 * Find the task that caused the abort and abort it first.
455 * The sequencer won't put anything on the done list until
456 * that happens.
457 */
483 tc_abort = *((u16*)(&dl->status_block[1])); 458 tc_abort = *((u16*)(&dl->status_block[1]));
484 tc_abort = le16_to_cpu(tc_abort); 459 tc_abort = le16_to_cpu(tc_abort);
485 460
486 ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", 461 list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
487 __FUNCTION__, dl->status_block[3]); 462 struct sas_task *task = ascb->uldd_task;
488 463
489 /* Find the pending task and abort it. */ 464 if (task && a->tc_index == tc_abort) {
490 list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) 465 failed_dev = task->dev;
491 if (a->tc_index == tc_abort) { 466 sas_task_abort(task);
492 task_kill_later(a);
493 break; 467 break;
494 } 468 }
469 }
470
471 if (!failed_dev) {
472 ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
473 __FUNCTION__, tc_abort);
474 goto out;
475 }
476
477 /*
478 * Now abort everything else for that device (hba?) so
479 * that the EH will wake up and do something.
480 */
481 list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
482 struct sas_task *task = ascb->uldd_task;
483
484 if (task &&
485 task->dev == failed_dev &&
486 a->tc_index != tc_abort)
487 sas_task_abort(task);
488 }
489
495 goto out; 490 goto out;
496 } 491 }
497 case REQ_DEVICE_RESET: { 492 case REQ_DEVICE_RESET: {
498 struct Scsi_Host *shost = sas_ha->core.shost;
499 struct sas_phy *dev_phy;
500 struct asd_ascb *a; 493 struct asd_ascb *a;
501 u16 conn_handle; 494 u16 conn_handle;
495 unsigned long flags;
496 struct sas_task *last_dev_task = NULL;
502 497
503 conn_handle = *((u16*)(&dl->status_block[1])); 498 conn_handle = *((u16*)(&dl->status_block[1]));
504 conn_handle = le16_to_cpu(conn_handle); 499 conn_handle = le16_to_cpu(conn_handle);
@@ -506,32 +501,47 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
506 ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, 501 ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__,
507 dl->status_block[3]); 502 dl->status_block[3]);
508 503
509 /* Kill all pending tasks and reset the device */ 504 /* Find the last pending task for the device... */
510 dev_phy = NULL;
511 list_for_each_entry(a, &asd_ha->seq.pend_q, list) { 505 list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
512 struct sas_task *task;
513 struct domain_device *dev;
514 u16 x; 506 u16 x;
507 struct domain_device *dev;
508 struct sas_task *task = a->uldd_task;
515 509
516 task = a->uldd_task;
517 if (!task) 510 if (!task)
518 continue; 511 continue;
519 dev = task->dev; 512 dev = task->dev;
520 513
521 x = (unsigned long)dev->lldd_dev; 514 x = (unsigned long)dev->lldd_dev;
522 if (x == conn_handle) { 515 if (x == conn_handle)
523 dev_phy = dev->port->phy; 516 last_dev_task = task;
524 task_kill_later(a);
525 }
526 } 517 }
527 518
528 /* Reset device port */ 519 if (!last_dev_task) {
529 if (!dev_phy) { 520 ASD_DPRINTK("%s: Device reset for idle device %d?\n",
530 ASD_DPRINTK("%s: No pending commands; can't reset.\n", 521 __FUNCTION__, conn_handle);
531 __FUNCTION__);
532 goto out; 522 goto out;
533 } 523 }
534 phy_reset_later(dev_phy, shost); 524
525 /* ...and set the reset flag */
526 spin_lock_irqsave(&last_dev_task->task_state_lock, flags);
527 last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
528 spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags);
529
530 /* Kill all pending tasks for the device */
531 list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
532 u16 x;
533 struct domain_device *dev;
534 struct sas_task *task = a->uldd_task;
535
536 if (!task)
537 continue;
538 dev = task->dev;
539
540 x = (unsigned long)dev->lldd_dev;
541 if (x == conn_handle)
542 sas_task_abort(task);
543 }
544
535 goto out; 545 goto out;
536 } 546 }
537 case SIGNAL_NCQ_ERROR: 547 case SIGNAL_NCQ_ERROR: