diff options
Diffstat (limited to 'drivers/ata/libata-eh.c')
-rw-r--r-- | drivers/ata/libata-eh.c | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e7e2ba24ce66..ac6ceed4bb60 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -56,6 +56,7 @@ enum { | |||
56 | */ | 56 | */ |
57 | enum { | 57 | enum { |
58 | ATA_EH_PRERESET_TIMEOUT = 10 * HZ, | 58 | ATA_EH_PRERESET_TIMEOUT = 10 * HZ, |
59 | ATA_EH_FASTDRAIN_INTERVAL = 3 * HZ, | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | /* The following table determines how we sequence resets. Each entry | 62 | /* The following table determines how we sequence resets. Each entry |
@@ -361,6 +362,9 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
361 | repeat: | 362 | repeat: |
362 | /* invoke error handler */ | 363 | /* invoke error handler */ |
363 | if (ap->ops->error_handler) { | 364 | if (ap->ops->error_handler) { |
365 | /* kill fast drain timer */ | ||
366 | del_timer_sync(&ap->fastdrain_timer); | ||
367 | |||
364 | /* process port resume request */ | 368 | /* process port resume request */ |
365 | ata_eh_handle_port_resume(ap); | 369 | ata_eh_handle_port_resume(ap); |
366 | 370 | ||
@@ -576,6 +580,94 @@ void ata_eng_timeout(struct ata_port *ap) | |||
576 | DPRINTK("EXIT\n"); | 580 | DPRINTK("EXIT\n"); |
577 | } | 581 | } |
578 | 582 | ||
583 | static int ata_eh_nr_in_flight(struct ata_port *ap) | ||
584 | { | ||
585 | unsigned int tag; | ||
586 | int nr = 0; | ||
587 | |||
588 | /* count only non-internal commands */ | ||
589 | for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) | ||
590 | if (ata_qc_from_tag(ap, tag)) | ||
591 | nr++; | ||
592 | |||
593 | return nr; | ||
594 | } | ||
595 | |||
596 | void ata_eh_fastdrain_timerfn(unsigned long arg) | ||
597 | { | ||
598 | struct ata_port *ap = (void *)arg; | ||
599 | unsigned long flags; | ||
600 | int cnt; | ||
601 | |||
602 | spin_lock_irqsave(ap->lock, flags); | ||
603 | |||
604 | cnt = ata_eh_nr_in_flight(ap); | ||
605 | |||
606 | /* are we done? */ | ||
607 | if (!cnt) | ||
608 | goto out_unlock; | ||
609 | |||
610 | if (cnt == ap->fastdrain_cnt) { | ||
611 | unsigned int tag; | ||
612 | |||
613 | /* No progress during the last interval, tag all | ||
614 | * in-flight qcs as timed out and freeze the port. | ||
615 | */ | ||
616 | for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) { | ||
617 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); | ||
618 | if (qc) | ||
619 | qc->err_mask |= AC_ERR_TIMEOUT; | ||
620 | } | ||
621 | |||
622 | ata_port_freeze(ap); | ||
623 | } else { | ||
624 | /* some qcs have finished, give it another chance */ | ||
625 | ap->fastdrain_cnt = cnt; | ||
626 | ap->fastdrain_timer.expires = | ||
627 | jiffies + ATA_EH_FASTDRAIN_INTERVAL; | ||
628 | add_timer(&ap->fastdrain_timer); | ||
629 | } | ||
630 | |||
631 | out_unlock: | ||
632 | spin_unlock_irqrestore(ap->lock, flags); | ||
633 | } | ||
634 | |||
635 | /** | ||
636 | * ata_eh_set_pending - set ATA_PFLAG_EH_PENDING and activate fast drain | ||
637 | * @ap: target ATA port | ||
638 | * @fastdrain: activate fast drain | ||
639 | * | ||
640 | * Set ATA_PFLAG_EH_PENDING and activate fast drain if @fastdrain | ||
641 | * is non-zero and EH wasn't pending before. Fast drain ensures | ||
642 | * that EH kicks in in timely manner. | ||
643 | * | ||
644 | * LOCKING: | ||
645 | * spin_lock_irqsave(host lock) | ||
646 | */ | ||
647 | static void ata_eh_set_pending(struct ata_port *ap, int fastdrain) | ||
648 | { | ||
649 | int cnt; | ||
650 | |||
651 | /* already scheduled? */ | ||
652 | if (ap->pflags & ATA_PFLAG_EH_PENDING) | ||
653 | return; | ||
654 | |||
655 | ap->pflags |= ATA_PFLAG_EH_PENDING; | ||
656 | |||
657 | if (!fastdrain) | ||
658 | return; | ||
659 | |||
660 | /* do we have in-flight qcs? */ | ||
661 | cnt = ata_eh_nr_in_flight(ap); | ||
662 | if (!cnt) | ||
663 | return; | ||
664 | |||
665 | /* activate fast drain */ | ||
666 | ap->fastdrain_cnt = cnt; | ||
667 | ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL; | ||
668 | add_timer(&ap->fastdrain_timer); | ||
669 | } | ||
670 | |||
579 | /** | 671 | /** |
580 | * ata_qc_schedule_eh - schedule qc for error handling | 672 | * ata_qc_schedule_eh - schedule qc for error handling |
581 | * @qc: command to schedule error handling for | 673 | * @qc: command to schedule error handling for |
@@ -593,7 +685,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) | |||
593 | WARN_ON(!ap->ops->error_handler); | 685 | WARN_ON(!ap->ops->error_handler); |
594 | 686 | ||
595 | qc->flags |= ATA_QCFLAG_FAILED; | 687 | qc->flags |= ATA_QCFLAG_FAILED; |
596 | qc->ap->pflags |= ATA_PFLAG_EH_PENDING; | 688 | ata_eh_set_pending(ap, 1); |
597 | 689 | ||
598 | /* The following will fail if timeout has already expired. | 690 | /* The following will fail if timeout has already expired. |
599 | * ata_scsi_error() takes care of such scmds on EH entry. | 691 | * ata_scsi_error() takes care of such scmds on EH entry. |
@@ -620,7 +712,7 @@ void ata_port_schedule_eh(struct ata_port *ap) | |||
620 | if (ap->pflags & ATA_PFLAG_INITIALIZING) | 712 | if (ap->pflags & ATA_PFLAG_INITIALIZING) |
621 | return; | 713 | return; |
622 | 714 | ||
623 | ap->pflags |= ATA_PFLAG_EH_PENDING; | 715 | ata_eh_set_pending(ap, 1); |
624 | scsi_schedule_eh(ap->scsi_host); | 716 | scsi_schedule_eh(ap->scsi_host); |
625 | 717 | ||
626 | DPRINTK("port EH scheduled\n"); | 718 | DPRINTK("port EH scheduled\n"); |
@@ -644,6 +736,9 @@ int ata_port_abort(struct ata_port *ap) | |||
644 | 736 | ||
645 | WARN_ON(!ap->ops->error_handler); | 737 | WARN_ON(!ap->ops->error_handler); |
646 | 738 | ||
739 | /* we're gonna abort all commands, no need for fast drain */ | ||
740 | ata_eh_set_pending(ap, 0); | ||
741 | |||
647 | for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { | 742 | for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { |
648 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); | 743 | struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); |
649 | 744 | ||