aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/libata-core.c62
-rw-r--r--drivers/scsi/libata-eh.c27
-rw-r--r--drivers/scsi/libata.h2
-rw-r--r--include/linux/libata.h27
4 files changed, 91 insertions, 27 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 1c34c1427aa..1f5c3270992 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -4123,6 +4123,66 @@ void __ata_qc_complete(struct ata_queued_cmd *qc)
qc->complete_fn(qc);
}
+/**
+ * ata_qc_complete - Complete an active ATA command
+ * @qc: Command to complete
+ * @err_mask: ATA Status register contents
+ *
+ * Indicate to the mid and upper layers that an ATA
+ * command has completed, with either an ok or not-ok status.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+void ata_qc_complete(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ /* XXX: New EH and old EH use different mechanisms to
+ * synchronize EH with regular execution path.
+ *
+ * In new EH, a failed qc is marked with ATA_QCFLAG_FAILED.
+ * Normal execution path is responsible for not accessing a
+ * failed qc. libata core enforces the rule by returning NULL
+ * from ata_qc_from_tag() for failed qcs.
+ *
+ * Old EH depends on ata_qc_complete() nullifying completion
+ * requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does
+ * not synchronize with interrupt handler. Only PIO task is
+ * taken care of.
+ */
+ if (ap->ops->error_handler) {
+ WARN_ON(ap->flags & ATA_FLAG_FROZEN);
+
+ if (unlikely(qc->err_mask))
+ qc->flags |= ATA_QCFLAG_FAILED;
+
+ if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) {
+ if (!ata_tag_internal(qc->tag)) {
+ /* always fill result TF for failed qc */
+ ap->ops->tf_read(ap, &qc->result_tf);
+ ata_qc_schedule_eh(qc);
+ return;
+ }
+ }
+
+ /* read result TF if requested */
+ if (qc->flags & ATA_QCFLAG_RESULT_TF)
+ ap->ops->tf_read(ap, &qc->result_tf);
+
+ __ata_qc_complete(qc);
+ } else {
+ if (qc->flags & ATA_QCFLAG_EH_SCHEDULED)
+ return;
+
+ /* read result TF if failed or requested */
+ if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF)
+ ap->ops->tf_read(ap, &qc->result_tf);
+
+ __ata_qc_complete(qc);
+ }
+}
+
static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
@@ -5245,7 +5305,7 @@ EXPORT_SYMBOL_GPL(ata_device_add);
EXPORT_SYMBOL_GPL(ata_host_set_remove);
EXPORT_SYMBOL_GPL(ata_sg_init);
EXPORT_SYMBOL_GPL(ata_sg_init_one);
-EXPORT_SYMBOL_GPL(__ata_qc_complete);
+EXPORT_SYMBOL_GPL(ata_qc_complete);
EXPORT_SYMBOL_GPL(ata_qc_issue_prot);
EXPORT_SYMBOL_GPL(ata_tf_load);
EXPORT_SYMBOL_GPL(ata_tf_read);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 959a1cdffac..471846fe4b7 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -210,6 +210,33 @@ void ata_eng_timeout(struct ata_port *ap)
DPRINTK("EXIT\n");
}
+/**
+ * ata_qc_schedule_eh - schedule qc for error handling
+ * @qc: command to schedule error handling for
+ *
+ * Schedule error handling for @qc. EH will kick in as soon as
+ * other commands are drained.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ */
+void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ WARN_ON(!ap->ops->error_handler);
+
+ qc->flags |= ATA_QCFLAG_FAILED;
+ qc->ap->flags |= ATA_FLAG_EH_PENDING;
+
+ /* The following will fail if timeout has already expired.
+ * ata_scsi_error() takes care of such scmds on EH entry.
+ * Note that ATA_QCFLAG_FAILED is unconditionally set after
+ * this function completes.
+ */
+ scsi_req_abort_cmd(qc->scsicmd);
+}
+
static void ata_eh_scsidone(struct scsi_cmnd *scmd)
{
/* nada */
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index c9ff83bbcfa..52622b7f8a9 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -57,6 +57,7 @@ extern int ata_do_reset(struct ata_port *ap, ata_reset_fn_t reset,
unsigned int *classes);
extern void ata_qc_free(struct ata_queued_cmd *qc);
extern void ata_qc_issue(struct ata_queued_cmd *qc);
+extern void __ata_qc_complete(struct ata_queued_cmd *qc);
extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
extern void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep);
@@ -101,5 +102,6 @@ extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
/* libata-eh.c */
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
+extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index bfcefdca061..6023f324e68 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -605,7 +605,7 @@ extern void ata_bmdma_start (struct ata_queued_cmd *qc);
extern void ata_bmdma_stop(struct ata_queued_cmd *qc);
extern u8 ata_bmdma_status(struct ata_port *ap);
extern void ata_bmdma_irq_clear(struct ata_port *ap);
-extern void __ata_qc_complete(struct ata_queued_cmd *qc);
+extern void ata_qc_complete(struct ata_queued_cmd *qc);
extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *));
extern int ata_std_bios_param(struct scsi_device *sdev,
@@ -883,31 +883,6 @@ static inline void ata_qc_reinit(struct ata_queued_cmd *qc)
}
/**
- * ata_qc_complete - Complete an active ATA command
- * @qc: Command to complete
- * @err_mask: ATA Status register contents
- *
- * Indicate to the mid and upper layers that an ATA
- * command has completed, with either an ok or not-ok status.
- *
- * LOCKING:
- * spin_lock_irqsave(host_set lock)
- */
-static inline void ata_qc_complete(struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
-
- if (unlikely(qc->flags & ATA_QCFLAG_EH_SCHEDULED))
- return;
-
- /* read result TF if failed or requested */
- if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF)
- ap->ops->tf_read(ap, &qc->result_tf);
-
- __ata_qc_complete(qc);
-}
-
-/**
* ata_irq_on - Enable interrupts on a port.
* @ap: Port on which interrupts are enabled.
*