diff options
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_scb.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 122 |
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 */ | ||
417 | static 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 | |||
432 | static 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... */ | ||
439 | static 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 | |||
450 | static void escb_tasklet_complete(struct asd_ascb *ascb, | 416 | static 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: |