aboutsummaryrefslogtreecommitdiff
path: root/drivers/ata/libata-eh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-eh.c')
-rw-r--r--drivers/ata/libata-eh.c150
1 files changed, 130 insertions, 20 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index a04488f0de88..0ea97c942ced 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -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
@@ -2667,14 +2676,14 @@ int ata_eh_reset(struct ata_link *link, int classify,
dev->pio_mode = XFER_PIO_0;
dev->flags &= ~ATA_DFLAG_SLEEPING;
- if (!ata_phys_link_offline(ata_dev_phys_link(dev))) {
- /* apply class override */
- if (lflags & ATA_LFLAG_ASSUME_ATA)
- classes[dev->devno] = ATA_DEV_ATA;
- else if (lflags & ATA_LFLAG_ASSUME_SEMB)
- classes[dev->devno] = ATA_DEV_SEMB_UNSUP;
- } else
- classes[dev->devno] = ATA_DEV_NONE;
+ if (ata_phys_link_offline(ata_dev_phys_link(dev)))
+ continue;
+
+ /* apply class override */
+ if (lflags & ATA_LFLAG_ASSUME_ATA)
+ classes[dev->devno] = ATA_DEV_ATA;
+ else if (lflags & ATA_LFLAG_ASSUME_SEMB)
+ classes[dev->devno] = ATA_DEV_SEMB_UNSUP;
}
/* record current link speed */
@@ -2713,34 +2722,48 @@ int ata_eh_reset(struct ata_link *link, int classify,
ap->pflags &= ~ATA_PFLAG_EH_PENDING;
spin_unlock_irqrestore(link->ap->lock, flags);
- /* Make sure onlineness and classification result correspond.
+ /*
+ * Make sure onlineness and classification result correspond.
* Hotplug could have happened during reset and some
* controllers fail to wait while a drive is spinning up after
* being hotplugged causing misdetection. By cross checking
- * link onlineness and classification result, those conditions
- * can be reliably detected and retried.
+ * link on/offlineness and classification result, those
+ * conditions can be reliably detected and retried.
*/
nr_unknown = 0;
ata_for_each_dev(dev, link, ALL) {
- /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */
- if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
- classes[dev->devno] = ATA_DEV_NONE;
- if (ata_phys_link_online(ata_dev_phys_link(dev)))
+ if (ata_phys_link_online(ata_dev_phys_link(dev))) {
+ if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
+ ata_dev_printk(dev, KERN_DEBUG, "link online "
+ "but device misclassifed\n");
+ classes[dev->devno] = ATA_DEV_NONE;
nr_unknown++;
+ }
+ } else if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
+ if (ata_class_enabled(classes[dev->devno]))
+ ata_dev_printk(dev, KERN_DEBUG, "link offline, "
+ "clearing class %d to NONE\n",
+ classes[dev->devno]);
+ classes[dev->devno] = ATA_DEV_NONE;
+ } else if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
+ ata_dev_printk(dev, KERN_DEBUG, "link status unknown, "
+ "clearing UNKNOWN to NONE\n");
+ classes[dev->devno] = ATA_DEV_NONE;
}
}
if (classify && nr_unknown) {
if (try < max_tries) {
ata_link_printk(link, KERN_WARNING, "link online but "
- "device misclassified, retrying\n");
+ "%d devices misclassified, retrying\n",
+ nr_unknown);
failed_link = link;
rc = -EAGAIN;
goto fail;
}
ata_link_printk(link, KERN_WARNING,
- "link online but device misclassified, "
- "device detection might fail\n");
+ "link online but %d devices misclassified, "
+ "device detection might fail\n", nr_unknown);
}
/* reset successful, schedule revalidation */
@@ -2967,12 +2990,14 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
* device detection messages backwards.
*/
ata_for_each_dev(dev, link, ALL) {
- if (!(new_mask & (1 << dev->devno)) ||
- dev->class == ATA_DEV_PMP)
+ if (!(new_mask & (1 << dev->devno)))
continue;
dev->class = ehc->classes[dev->devno];
+ if (dev->class == ATA_DEV_PMP)
+ continue;
+
ehc->i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ehc->i.flags &= ~ATA_EHI_PRINTINFO;
@@ -3096,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;
@@ -3439,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)