summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libata-core.c
diff options
context:
space:
mode:
authorTejun Heo2006-05-15 13:58:05 +0200
committerTejun Heo2006-05-15 13:58:05 +0200
commitf686bcb8078ac7505ec88818886c2c72639f4fc5 (patch)
tree7c563e0032b2d85b631b617b8bb2a7a648607468 /drivers/scsi/libata-core.c
parent[PATCH] libata-eh-fw: update ata_qc_from_tag() to enforce normal/EH qc ownership (diff)
downloadkernel-qcow2-linux-f686bcb8078ac7505ec88818886c2c72639f4fc5.tar.gz
kernel-qcow2-linux-f686bcb8078ac7505ec88818886c2c72639f4fc5.tar.xz
kernel-qcow2-linux-f686bcb8078ac7505ec88818886c2c72639f4fc5.zip
[PATCH] libata-eh-fw: implement new EH scheduling via error completion
There are several ways a qc can get schedule for EH in new EH. This patch implements one of them - completing a qc with ATA_QCFLAG_FAILED set or with non-zero qc->err_mask. ALL such qc's are examined by EH. New EH schedules a qc for EH from completion iff ->error_handler is implemented, qc is marked as failed or qc->err_mask is non-zero and the command is not an internal command (internal cmd is handled via ->post_internal_cmd). The EH scheduling itself is performed by asking SCSI midlayer to schedule EH for the specified scmd. For drivers implementing old-EH, nothing changes. As this change makes ata_qc_complete() rather large, it's not inlined anymore and __ata_qc_complete() is exported to other parts of libata for later use. Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers/scsi/libata-core.c')
-rw-r--r--drivers/scsi/libata-core.c62
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 1c34c1427aa3..1f5c3270992a 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);