Merge branch 'tc1100-wmi' into release
[deliverable/linux.git] / drivers / ata / libata-eh.c
index bba2ae5df1c2270664c7b9c0c19f94ca4eefb63b..0ea97c942ceda3485670021b18b9721802949391 100644 (file)
@@ -110,6 +110,13 @@ static const unsigned long ata_eh_identify_timeouts[] = {
        ULONG_MAX,
 };
 
+static const unsigned long ata_eh_flush_timeouts[] = {
+       15000,  /* be generous with flush */
+       15000,  /* ditto */
+       30000,  /* and even more generous */
+       ULONG_MAX,
+};
+
 static const unsigned long ata_eh_other_timeouts[] = {
         5000,  /* same rationale as identify timeout */
        10000,  /* ditto */
@@ -147,6 +154,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = {
          .timeouts = ata_eh_other_timeouts, },
        { .commands = CMDS(ATA_CMD_INIT_DEV_PARAMS),
          .timeouts = ata_eh_other_timeouts, },
+       { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
+         .timeouts = ata_eh_flush_timeouts },
 };
 #undef CMDS
 
@@ -3112,6 +3121,82 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
        return 0;
 }
 
+/**
+ *     ata_eh_maybe_retry_flush - Retry FLUSH if necessary
+ *     @dev: ATA device which may need FLUSH retry
+ *
+ *     If @dev failed FLUSH, it needs to be reported upper layer
+ *     immediately as it means that @dev failed to remap and already
+ *     lost at least a sector and further FLUSH retrials won't make
+ *     any difference to the lost sector.  However, if FLUSH failed
+ *     for other reasons, for example transmission error, FLUSH needs
+ *     to be retried.
+ *
+ *     This function determines whether FLUSH failure retry is
+ *     necessary and performs it if so.
+ *
+ *     RETURNS:
+ *     0 if EH can continue, -errno if EH needs to be repeated.
+ */
+static int ata_eh_maybe_retry_flush(struct ata_device *dev)
+{
+       struct ata_link *link = dev->link;
+       struct ata_port *ap = link->ap;
+       struct ata_queued_cmd *qc;
+       struct ata_taskfile tf;
+       unsigned int err_mask;
+       int rc = 0;
+
+       /* did flush fail for this device? */
+       if (!ata_tag_valid(link->active_tag))
+               return 0;
+
+       qc = __ata_qc_from_tag(ap, link->active_tag);
+       if (qc->dev != dev || (qc->tf.command != ATA_CMD_FLUSH_EXT &&
+                              qc->tf.command != ATA_CMD_FLUSH))
+               return 0;
+
+       /* if the device failed it, it should be reported to upper layers */
+       if (qc->err_mask & AC_ERR_DEV)
+               return 0;
+
+       /* flush failed for some other reason, give it another shot */
+       ata_tf_init(dev, &tf);
+
+       tf.command = qc->tf.command;
+       tf.flags |= ATA_TFLAG_DEVICE;
+       tf.protocol = ATA_PROT_NODATA;
+
+       ata_dev_printk(dev, KERN_WARNING, "retrying FLUSH 0x%x Emask 0x%x\n",
+                      tf.command, qc->err_mask);
+
+       err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+       if (!err_mask) {
+               /*
+                * FLUSH is complete but there's no way to
+                * successfully complete a failed command from EH.
+                * Making sure retry is allowed at least once and
+                * retrying it should do the trick - whatever was in
+                * the cache is already on the platter and this won't
+                * cause infinite loop.
+                */
+               qc->scsicmd->allowed = max(qc->scsicmd->allowed, 1);
+       } else {
+               ata_dev_printk(dev, KERN_WARNING, "FLUSH failed Emask 0x%x\n",
+                              err_mask);
+               rc = -EIO;
+
+               /* if device failed it, report it to upper layers */
+               if (err_mask & AC_ERR_DEV) {
+                       qc->err_mask |= AC_ERR_DEV;
+                       qc->result_tf = tf;
+                       if (!(ap->pflags & ATA_PFLAG_FROZEN))
+                               rc = 0;
+               }
+       }
+       return rc;
+}
+
 static int ata_link_nr_enabled(struct ata_link *link)
 {
        struct ata_device *dev;
@@ -3455,6 +3540,15 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                        }
                }
 
+               /* retry flush if necessary */
+               ata_for_each_dev(dev, link, ALL) {
+                       if (dev->class != ATA_DEV_ATA)
+                               continue;
+                       rc = ata_eh_maybe_retry_flush(dev);
+                       if (rc)
+                               goto dev_fail;
+               }
+
                /* configure link power saving */
                if (ehc->i.action & ATA_EH_LPM)
                        ata_for_each_dev(dev, link, ALL)
This page took 0.030259 seconds and 5 git commands to generate.