aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-07-16 01:29:41 -0400
committerJeff Garzik <jeff@garzik.org>2007-07-20 08:26:26 -0400
commit5ddf24c5ea9d715dc4f5d5d5dd1c9337d90466dc (patch)
tree6ba89094decfada468fba1f4670b9395c6f66e42 /drivers
parent4e57c517b3cbaceb7438eeec879ca129fc17442c (diff)
libata: implement EH fast drain
In most cases, when EH is scheduled, all in-flight commands are aborted causing EH to kick in immediately. However, in some cases (especially with PMP), it's unclear which commands are affected by the error condition and although aborting all in-flight commands work, it isn't optimal and may cause unnecessary disruption. On the other hand, waiting for in-flight commands to drain themselves can take up to 30seconds. This patch implements EH fast drain to handle such situations. It gives in-flight commands some time to finish up but doesn't wait for too long. After EH is scheduled, fast drain timer is started and if no other completion occurs in ATA_EH_FASTDRAIN_INTERVAL all in-flight commands are aborted. If any completion occurred in the interval, the port is given another interval to finish up itself. Currently ATA_EH_FASTDRAIN_INTERVAL is 3 secs which should be enough for finishing up most commands. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/libata-core.c3
-rw-r--r--drivers/ata/libata-eh.c99
-rw-r--r--drivers/ata/libata.h1
3 files changed, 101 insertions, 2 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 35b621293831..6001aae0b884 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6077,6 +6077,9 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
6077 INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); 6077 INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
6078 INIT_LIST_HEAD(&ap->eh_done_q); 6078 INIT_LIST_HEAD(&ap->eh_done_q);
6079 init_waitqueue_head(&ap->eh_wait_q); 6079 init_waitqueue_head(&ap->eh_wait_q);
6080 init_timer_deferrable(&ap->fastdrain_timer);
6081 ap->fastdrain_timer.function = ata_eh_fastdrain_timerfn;
6082 ap->fastdrain_timer.data = (unsigned long)ap;
6080 6083
6081 ap->cbl = ATA_CBL_NONE; 6084 ap->cbl = ATA_CBL_NONE;
6082 6085
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 */
57enum { 57enum {
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
583static 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
596void 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 */
647static 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
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 48836b22ce2f..564cd234c805 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -151,6 +151,7 @@ extern int ata_bus_probe(struct ata_port *ap);
151extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); 151extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
152extern void ata_scsi_error(struct Scsi_Host *host); 152extern void ata_scsi_error(struct Scsi_Host *host);
153extern void ata_port_wait_eh(struct ata_port *ap); 153extern void ata_port_wait_eh(struct ata_port *ap);
154extern void ata_eh_fastdrain_timerfn(unsigned long arg);
154extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); 155extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
155 156
156/* libata-sff.c */ 157/* libata-sff.c */