summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/libata-eh.c1
-rw-r--r--drivers/ata/libata-sata.c27
2 files changed, 28 insertions, 0 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index bde15f855f70..34303ce67c14 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1955,6 +1955,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
ata_qc_for_each_raw(ap, qc, tag) {
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
+ qc->flags & ATA_QCFLAG_RETRY ||
ata_dev_phys_link(qc->dev) != link)
continue;
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c
index 283ce1ab29cf..18ef14e749a0 100644
--- a/drivers/ata/libata-sata.c
+++ b/drivers/ata/libata-sata.c
@@ -1476,6 +1476,33 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
}
}
+ ata_qc_for_each_raw(ap, qc, tag) {
+ if (!(qc->flags & ATA_QCFLAG_FAILED) ||
+ ata_dev_phys_link(qc->dev) != link)
+ continue;
+
+ /* Skip the single QC which caused the NCQ error. */
+ if (qc->err_mask)
+ continue;
+
+ /*
+ * For SATA, the STATUS and ERROR fields are shared for all NCQ
+ * commands that were completed with the same SDB FIS.
+ * Therefore, we have to clear the ATA_ERR bit for all QCs
+ * except the one that caused the NCQ error.
+ */
+ qc->result_tf.status &= ~ATA_ERR;
+ qc->result_tf.error = 0;
+
+ /*
+ * If we get a NCQ error, that means that a single command was
+ * aborted. All other failed commands for our link should be
+ * retried and has no business of going though further scrutiny
+ * by ata_eh_link_autopsy().
+ */
+ qc->flags |= ATA_QCFLAG_RETRY;
+ }
+
ehc->i.err_mask &= ~AC_ERR_DEV;
}
EXPORT_SYMBOL_GPL(ata_eh_analyze_ncq_error);