[PATCH] libata: implement and apply ata_eh_qc_complete/retry()
authorTejun Heo <htejun@gmail.com>
Mon, 23 Jan 2006 04:09:37 +0000 (13:09 +0900)
committerJeff Garzik <jgarzik@pobox.com>
Fri, 27 Jan 2006 03:36:28 +0000 (22:36 -0500)
Implement ata_eh_qc_complete/retry() using scsi_eh_finish_cmd() and
scsi_eh_flush_done_q().  This removes all eh scsicmd finish hacks from
low level drivers.

This change was first suggested by Jeff Garzik.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
drivers/scsi/ahci.c
drivers/scsi/libata-core.c
drivers/scsi/libata-scsi.c
drivers/scsi/sata_mv.c
drivers/scsi/sata_promise.c
drivers/scsi/sata_sil24.c
drivers/scsi/sata_sx4.c
include/linux/libata.h

index 0f6e4dd22901988bf719ffbb5dc50ee2f0590be0..5a6b23009897083cc1c5b690666f051d34f0db47 100644 (file)
@@ -672,19 +672,13 @@ static void ahci_eng_timeout(struct ata_port *ap)
                       ap->id);
        } else {
                ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
-
-               /* hack alert!  We cannot use the supplied completion
-                * function from inside the ->eh_strategy_handler() thread.
-                * libata is the only user of ->eh_strategy_handler() in
-                * any kernel, so the default scsi_done() assumes it is
-                * not being called from the SCSI EH.
-                */
-               qc->scsidone = scsi_finish_command;
                qc->err_mask |= AC_ERR_TIMEOUT;
-               ata_qc_complete(qc);
        }
 
        spin_unlock_irqrestore(&host_set->lock, flags);
+
+       if (qc)
+               ata_eh_qc_complete(qc);
 }
 
 static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
index b29bf0dc948a72b6c0b4fe258319e7ad4f3d75da..93ae2dcc837c183c41f367ad3c1c9d8688a169d6 100644 (file)
@@ -3449,14 +3449,6 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
 
        spin_lock_irqsave(&host_set->lock, flags);
 
-       /* hack alert!  We cannot use the supplied completion
-        * function from inside the ->eh_strategy_handler() thread.
-        * libata is the only user of ->eh_strategy_handler() in
-        * any kernel, so the default scsi_done() assumes it is
-        * not being called from the SCSI EH.
-        */
-       qc->scsidone = scsi_finish_command;
-
        switch (qc->tf.protocol) {
 
        case ATA_PROT_DMA:
@@ -3480,12 +3472,13 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
 
                /* complete taskfile transaction */
                qc->err_mask |= ac_err_mask(drv_stat);
-               ata_qc_complete(qc);
                break;
        }
 
        spin_unlock_irqrestore(&host_set->lock, flags);
 
+       ata_eh_qc_complete(qc);
+
        DPRINTK("EXIT\n");
 }
 
@@ -4422,6 +4415,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
 
        INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
        INIT_WORK(&ap->pio_task, ata_pio_task, ap);
+       INIT_LIST_HEAD(&ap->eh_done_q);
 
        for (i = 0; i < ATA_MAX_DEVICES; i++)
                ap->device[i].devno = i;
@@ -5165,6 +5159,8 @@ EXPORT_SYMBOL_GPL(ata_dev_classify);
 EXPORT_SYMBOL_GPL(ata_dev_id_string);
 EXPORT_SYMBOL_GPL(ata_dev_config);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
+EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
+EXPORT_SYMBOL_GPL(ata_eh_qc_retry);
 
 EXPORT_SYMBOL_GPL(ata_pio_need_iordy);
 EXPORT_SYMBOL_GPL(ata_timing_compute);
index 95e3c278dd4354b3d42d434f60abccf932fbd703..ab6b53349d6f7c365c3bc58e1c0475c9587e5dc8 100644 (file)
@@ -738,17 +738,64 @@ int ata_scsi_error(struct Scsi_Host *host)
        ap = (struct ata_port *) &host->hostdata[0];
        ap->ops->eng_timeout(ap);
 
-       /* TODO: this is per-command; when queueing is supported
-        * this code will either change or move to a more
-        * appropriate place
-        */
-       host->host_failed--;
-       INIT_LIST_HEAD(&host->eh_cmd_q);
+       assert(host->host_failed == 0 && list_empty(&host->eh_cmd_q));
+
+       scsi_eh_flush_done_q(&ap->eh_done_q);
 
        DPRINTK("EXIT\n");
        return 0;
 }
 
+static void ata_eh_scsidone(struct scsi_cmnd *scmd)
+{
+       /* nada */
+}
+
+static void __ata_eh_qc_complete(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       struct scsi_cmnd *scmd = qc->scsicmd;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ap->host_set->lock, flags);
+       qc->scsidone = ata_eh_scsidone;
+       ata_qc_complete(qc);
+       assert(!ata_tag_valid(qc->tag));
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+       scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
+}
+
+/**
+ *     ata_eh_qc_complete - Complete an active ATA command from EH
+ *     @qc: Command to complete
+ *
+ *     Indicate to the mid and upper layers that an ATA command has
+ *     completed.  To be used from EH.
+ */
+void ata_eh_qc_complete(struct ata_queued_cmd *qc)
+{
+       struct scsi_cmnd *scmd = qc->scsicmd;
+       scmd->retries = scmd->allowed;
+       __ata_eh_qc_complete(qc);
+}
+
+/**
+ *     ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH
+ *     @qc: Command to retry
+ *
+ *     Indicate to the mid and upper layers that an ATA command
+ *     should be retried.  To be used from EH.
+ *
+ *     SCSI midlayer limits the number of retries to scmd->allowed.
+ *     This function might need to adjust scmd->retries for commands
+ *     which get retried due to unrelated NCQ failures.
+ */
+void ata_eh_qc_retry(struct ata_queued_cmd *qc)
+{
+       __ata_eh_qc_complete(qc);
+}
+
 /**
  *     ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command
  *     @qc: Storage for translated ATA taskfile
index 281223a0e45fd1d12c5af902ba78ce9144cef503..d9a554ca45c766eb18b5245e12e59fe1877cb5cb 100644 (file)
@@ -1839,7 +1839,6 @@ static void mv_phy_reset(struct ata_port *ap)
 static void mv_eng_timeout(struct ata_port *ap)
 {
        struct ata_queued_cmd *qc;
-       unsigned long flags;
 
        printk(KERN_ERR "ata%u: Entering mv_eng_timeout\n",ap->id);
        DPRINTK("All regs @ start of eng_timeout\n");
@@ -1858,17 +1857,8 @@ static void mv_eng_timeout(struct ata_port *ap)
                printk(KERN_ERR "ata%u: BUG: timeout without command\n",
                       ap->id);
        } else {
-               /* hack alert!  We cannot use the supplied completion
-                * function from inside the ->eh_strategy_handler() thread.
-                * libata is the only user of ->eh_strategy_handler() in
-                * any kernel, so the default scsi_done() assumes it is
-                * not being called from the SCSI EH.
-                */
-               spin_lock_irqsave(&ap->host_set->lock, flags);
-               qc->scsidone = scsi_finish_command;
                qc->err_mask |= AC_ERR_TIMEOUT;
-               ata_qc_complete(qc);
-               spin_unlock_irqrestore(&ap->host_set->lock, flags);
+               ata_eh_qc_complete(qc);
        }
 }
 
index bac36d5b7c3edef0c79b70e2cb754aff56b291d4..77ef646b0f9299cab38a847389e30e67c3822282 100644 (file)
@@ -400,21 +400,12 @@ static void pdc_eng_timeout(struct ata_port *ap)
                goto out;
        }
 
-       /* hack alert!  We cannot use the supplied completion
-        * function from inside the ->eh_strategy_handler() thread.
-        * libata is the only user of ->eh_strategy_handler() in
-        * any kernel, so the default scsi_done() assumes it is
-        * not being called from the SCSI EH.
-        */
-       qc->scsidone = scsi_finish_command;
-
        switch (qc->tf.protocol) {
        case ATA_PROT_DMA:
        case ATA_PROT_NODATA:
                printk(KERN_ERR "ata%u: command timeout\n", ap->id);
                drv_stat = ata_wait_idle(ap);
                qc->err_mask |= __ac_err_mask(drv_stat);
-               ata_qc_complete(qc);
                break;
 
        default:
@@ -424,12 +415,13 @@ static void pdc_eng_timeout(struct ata_port *ap)
                       ap->id, qc->tf.command, drv_stat);
 
                qc->err_mask |= ac_err_mask(drv_stat);
-               ata_qc_complete(qc);
                break;
        }
 
 out:
        spin_unlock_irqrestore(&host_set->lock, flags);
+       if (qc)
+               ata_eh_qc_complete(qc);
        DPRINTK("EXIT\n");
 }
 
index 5a7a7b1d4add04dde8677fb156551a1fdb5d6334..7222fc7ff3fc49c1e59601133624b51d1584f259 100644 (file)
@@ -644,17 +644,9 @@ static void sil24_eng_timeout(struct ata_port *ap)
                return;
        }
 
-       /*
-        * hack alert!  We cannot use the supplied completion
-        * function from inside the ->eh_strategy_handler() thread.
-        * libata is the only user of ->eh_strategy_handler() in
-        * any kernel, so the default scsi_done() assumes it is
-        * not being called from the SCSI EH.
-        */
        printk(KERN_ERR "ata%u: command timeout\n", ap->id);
-       qc->scsidone = scsi_finish_command;
        qc->err_mask |= AC_ERR_TIMEOUT;
-       ata_qc_complete(qc);
+       ata_eh_qc_complete(qc);
 
        sil24_reset_controller(ap);
 }
index 3175c6bb4fecd6a9cae28e20cedba259b443ef40..9f992fbcf2e7db5c3b33ca95b4aeccd3a59d3fcf 100644 (file)
@@ -872,20 +872,11 @@ static void pdc_eng_timeout(struct ata_port *ap)
                goto out;
        }
 
-       /* hack alert!  We cannot use the supplied completion
-        * function from inside the ->eh_strategy_handler() thread.
-        * libata is the only user of ->eh_strategy_handler() in
-        * any kernel, so the default scsi_done() assumes it is
-        * not being called from the SCSI EH.
-        */
-       qc->scsidone = scsi_finish_command;
-
        switch (qc->tf.protocol) {
        case ATA_PROT_DMA:
        case ATA_PROT_NODATA:
                printk(KERN_ERR "ata%u: command timeout\n", ap->id);
                qc->err_mask |= __ac_err_mask(ata_wait_idle(ap));
-               ata_qc_complete(qc);
                break;
 
        default:
@@ -895,12 +886,13 @@ static void pdc_eng_timeout(struct ata_port *ap)
                       ap->id, qc->tf.command, drv_stat);
 
                qc->err_mask |= ac_err_mask(drv_stat);
-               ata_qc_complete(qc);
                break;
        }
 
 out:
        spin_unlock_irqrestore(&host_set->lock, flags);
+       if (qc)
+               ata_eh_qc_complete(qc);
        DPRINTK("EXIT\n");
 }
 
index b1ea2f98bfbb3aaf261fddc7802930160bd3493d..576788de962a3f028883347d7a60a3d1929872e4 100644 (file)
@@ -398,6 +398,7 @@ struct ata_port {
        unsigned long           pio_task_timeout;
 
        u32                     msg_enable;
+       struct list_head        eh_done_q;
 
        void                    *private_data;
 };
@@ -490,6 +491,8 @@ extern int ata_scsi_detect(struct scsi_host_template *sht);
 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
 extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *));
 extern int ata_scsi_error(struct Scsi_Host *host);
+extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
+extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
 extern int ata_scsi_release(struct Scsi_Host *host);
 extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
 extern int ata_scsi_device_resume(struct scsi_device *);