linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHSET] new EH framework
@ 2006-04-02 18:31 Tejun Heo
  2006-04-02 18:31 ` [PATCH 04/13] libata: implement ata_port_freeze() Tejun Heo
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide, htejun

Hello, all.

New EH, finally.  New EH will be posted as two patchsets -
eh-framework and eh.  As the name suggests, the first one implements
EH framework in libata core layer and the second one implements
helpers, drivers, stock routines for bmdma controllers and converts
several drivers (ata_piix, ahci, sata_sil) to new EH.

This is the first take of eh-framework patchset.  This patchset
contains 13 patches and against

  upstream [1]
  + scsi_eh_schedule patchset, take 2 [2][3]
  + ahci softreset presence detection patch [4]

Brief description of new EH framework follows.


1. Introduction
---------------

In the old EH, there is only one way a qc gets EH'd - timeout, so
LLDDs handled errors which are not timeout either directly from
interrupt handler or by letting it timeout, which is at best
inefficient.  Worse than that, normal execution (irq handler) <-> EH
synchronization wasn't taken into account.  New EH tries to acheive
the following goals.

 a. Clear ownership of qc.  If normal execution path owns a qc, it
    owns it.  Once a qc is taken over by EH, EH owns it.  No one but
    owner can access the qc.

 b. Resistant against weird hardware behavior.  Some controllers
    and/or devices can enter states where they violate a lot of driver
    assumptions.  In new EH, whenever a controller or device acts in
    unexpected manner (HSM violation), the port gets frozen and no
    access is allowed it gets successfully reset.

 c. Threaded/unified implementation.  Recovery actions involve a lot
    of waiting and retries in nature and doesn't have to be super
    efficient.  Do most stuff in EH context.  This also allows EH
    implementation to be implemented as unified executon flow making
    it easier to implement and maintain.

 d. Have to co-exist with old EH till all LLDDs get converted.


2. Who owns qc and how it gets transferred
------------------------------------------

All qc's start owned by normal execution path.  Depending on protocol,
it can be the interrupt handler or PIO task.  There are several ways
the ownership can be transferred to EH.

 a. Completing with an error.  If a qc gets completed with non-zero
    err_mask, ata_qc_complete() automatically schedules the qc for EH.
    After ata_qc_complete() completes, the qc is owned by EH.  Normal
    execution path is not allowed to access it.

 b. Timing out.  When a qc times out, the qc is secheduled for EH.
    Also, timeout condition is considered as HSM violation as we don't
    really know in what state the controller and device are in.  So,
    timeout triggers mass abortion and freezes the port.

 c. Mass abortion.  This schedules all active qc's for EH.  This is
    used when an exception which affects all commands occur.  e.g.
    NCQ command failure or HSM violation.  Freezing a port implies
    mass abortion.

When a qc gets scheduled for EH, ATA_QCFLAG_EH is set atomically.
libata core layer enforces EH ownership by returning NULL from
ata_qc_from_tag() for the qc.


3. EH execution
---------------

EH starts execution when there is no qc left executing in normal path.
ie. On entry to EH, all qc's are owned by EH and normal execution path
is not allowed to / cannot access those qc's.  Because PIO task is not
synchronized with host_set lock, synchronization with PIO task is
achieved by flushing the port task prior to entering ->error_handler,
but the end result is the same.

If EH got invoked due to exceptions which are not HSM violation, the
port should be quiescent at this point.  If HSM violation occurred,
the ports must have been frozen, so, again, the port is quiescent.

EH examines the situation and perform recovery actions.  Frozen port
is thawed by resetting it, ATAPI sense is requested, so on.  EH issues
all recovery commands using ata_exec_internal() which uses separate
reserved qc such that it can be executed without cannibalising failed
qc's.

Failed qc's are completed or retries only after all EH actions are
complete when EH knows that the port is in known state and sg table,
data buffers and such are safe to deallocate.


4. Frozen
---------

A port is frozen whenever libata cannot determine in what state the
port is in.  While frozen, no one should access the controller and
attached devices.  Ideally this can be implemented by masking
interrupt from the port.  If that is not possible, the LLDD's
interrupt handler is responsible for unconditionally acking and
clearing all interrupts which occur while the port is frozen.

All resets are done while the target port is frozen including resets
performed during probing.  A port starts frozen and gets thawed after
the first probing reset is complete.


5. Notes
--------

* ata_down_xfermask_limit()

  As said above, during EH, no normal execution occurs.  There is no
  active qc during EH except for internal command which is issued only
  after the device is put into known state.  So, altering
  dev->*_mask's during EH is safe.


Thanks.

--
tejun

[1] 6d5f9732a16a74d75f8cdba5b00557662e83f466
[2] http://marc.theaimsgroup.com/?l=linux-scsi&m=114399387517874&w=2
[3] http://marc.theaimsgroup.com/?l=linux-ide&m=114399407718154&w=2
[4] http://marc.theaimsgroup.com/?l=linux-ide&m=114399712126232&w=2



^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 02/13] libata: add new EH operations
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
  2006-04-02 18:31 ` [PATCH 04/13] libata: implement ata_port_freeze() Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 07/13] libata: implement ata_eh_schedule_port() Tejun Heo
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

This patch adds three operations for new EH.  The operations are...

->freeze		freeze the port
->error_handler		new EH
->post_internal_cmd	EH/clean up for internal commands

The old and new EH's have to live together for the time being and
->error_handler will be used as a switch to select which EH mechanism
to use.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 include/linux/libata.h |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)

9ee247ea9ddf4f1d0f5d7d2e457c8b4edd9c54f6
diff --git a/include/linux/libata.h b/include/linux/libata.h
index cc57c4d..0858788 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -463,7 +463,14 @@ struct ata_port_operations {
 	void (*qc_prep) (struct ata_queued_cmd *qc);
 	unsigned int (*qc_issue) (struct ata_queued_cmd *qc);
 
-	void (*eng_timeout) (struct ata_port *ap);
+	/* Error handlers.  ->error_handler overrides ->eng_timeout and
+	 * indicates that new-style EH is in place.
+	 */
+	void (*eng_timeout) (struct ata_port *ap); /* obsolete */
+
+	void (*freeze) (struct ata_port *ap);
+	void (*error_handler) (struct ata_port *ap);
+	void (*post_internal_cmd) (struct ata_queued_cmd *qc);
 
 	irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
 	void (*irq_clear) (struct ata_port *);
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 01/13] libata: add flags for new EH
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (6 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 05/13] libata: update ata_qc_from_tag() to enforce normal/EH qc ownership Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 12/13] libata: update SCSI command completion path " Tejun Heo
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Add new device, port and ac flags to be used for new EH.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 include/linux/libata.h |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

de9fe3323707d4395862b90fa728f310c18f80c8
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 75bdee0..cc57c4d 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -125,6 +125,7 @@ enum {
 	ATA_DFLAG_CFG_MASK	= (1 << 8) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 8), /* device currently in PIO mode */
+	ATA_DFLAG_FAILED	= (1 << 9), /* device has failed */
 
 	ATA_DEV_UNKNOWN		= 0,	/* unknown device */
 	ATA_DEV_ATA		= 1,	/* ATA device */
@@ -152,7 +153,8 @@ enum {
 	ATA_FLAG_FLUSH_PORT_TASK = (1 << 18), /* flush port task */
 
 	ATA_FLAG_DISABLED	= (1 << 19), /* port is disabled, ignore it */
-	ATA_FLAG_SUSPENDED	= (1 << 20), /* port is suspended */
+	ATA_FLAG_FROZEN		= (1 << 20), /* port is frozen */
+	ATA_FLAG_SUSPENDED	= (1 << 21), /* port is suspended (power) */
 
 	/* bits 24:31 of ap->flags are reserved for LLDD specific flags */
 
@@ -162,7 +164,10 @@ enum {
 	ATA_QCFLAG_SINGLE	= (1 << 2), /* no s/g, just a single buffer */
 	ATA_QCFLAG_DMAMAP	= ATA_QCFLAG_SG | ATA_QCFLAG_SINGLE,
 	ATA_QCFLAG_IO		= (1 << 3), /* standard IO command */
-	ATA_QCFLAG_EH_SCHEDULED = (1 << 4), /* EH scheduled */
+	ATA_QCFLAG_TIMEOUT	= (1 << 4), /* cmd has timed out */
+	ATA_QCFLAG_FAILED	= (1 << 5), /* cmd has failed */
+	ATA_QCFLAG_EH_SCHEDULED	= (1 << 6), /* EH scheduled */
+	ATA_QCFLAG_SENSE_VALID	= (1 << 7), /* sense data valid */
 
 	/* host set flags */
 	ATA_HOST_SIMPLEX	= (1 << 0),	/* Host is simplex, one DMA channel per host_set only */
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/13] libata: implement ata_eh_schedule_port()
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
  2006-04-02 18:31 ` [PATCH 04/13] libata: implement ata_port_freeze() Tejun Heo
  2006-04-02 18:31 ` [PATCH 02/13] libata: add new EH operations Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 08/13] libata: implement new EH scheduling via timeout Tejun Heo
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

ata_eh_schedule_port() is the gateway to EH and does one of the
followings depending on how it's invoked.

* Without any flag: It simply schedules EH.  EH will kick in after all
  commands are drained (unless another event occurs, of course).

* ATA_EH_ABORT: EH is scheduled and all currently active qc's get
  aborted.  The caller is responsible for making sure the controller
  and devices are in stable state in this case.

* ATA_EH_FREEZE: It does everything ATA_EH_ABORT does and then freezes
  the port; thus, making the port inaccessible until it gets reset.
  This can be used to safely schedule EH when an HSM violation event
  occurs.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |    1 +
 drivers/scsi/libata-eh.c   |   52 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/libata.h     |    5 ++++
 3 files changed, 58 insertions(+), 0 deletions(-)

c31b07c28393b6a586dc1e1bcb5809f5c8e3be79
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 600b323..735f328 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5245,5 +5245,6 @@ EXPORT_SYMBOL_GPL(ata_scsi_device_resume
 
 EXPORT_SYMBOL_GPL(ata_scsi_error);
 EXPORT_SYMBOL_GPL(ata_eng_timeout);
+EXPORT_SYMBOL_GPL(ata_eh_schedule_port);
 EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
 EXPORT_SYMBOL_GPL(ata_eh_qc_retry);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index a1fe14f..d443ef2 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -239,6 +239,58 @@ void ata_eh_schedule_qc(struct ata_queue
 	scsi_eh_schedule_cmd(qc->scsicmd);
 }
 
+/**
+ *	ata_eh_schedule_port - schedule error handling without a qc
+ *	@ap: ATA port to schedule EH for
+ *	@flags: ATA_EH_* flags
+ *
+ *	Schedule error hanlding for the speficied ATA port.  EH will
+ *	kick in as soon as all commands are drained.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_eh_schedule_port(struct ata_port *ap, unsigned int flags)
+{
+	int internal_cmd = ata_tag_internal(ap->active_tag);
+	int i;
+
+	WARN_ON(!ap->ops->error_handler);
+
+	if (!internal_cmd)
+		scsi_eh_schedule_host(ap->host);
+
+	if (!(flags & (ATA_EH_ABORT | ATA_EH_FREEZE)))
+		return;
+
+	for (i = 0; i < ATA_MAX_QUEUE; i++) {
+		struct ata_queued_cmd *qc = ata_qc_from_tag(ap, i);
+		if (qc) {
+			if (!internal_cmd)
+				ata_eh_schedule_qc(qc);
+			else {
+				qc->flags |= ATA_QCFLAG_FAILED;
+				__ata_qc_complete(qc);
+			}
+		}
+	}
+
+	if (!(flags & ATA_EH_FREEZE))
+		return;
+
+	/* Timeout handler might try to freeze an already frozen port
+	 * if it races against interrupt handler or another timeout.
+	 * Such races should be _very_ rare.  Whine and ignore.
+	 */
+	if (ap->flags & ATA_FLAG_FROZEN) {
+		printk(KERN_INFO "ata%u: ata_eh_schedule_port invoked on "
+		       "a frozen port\n", ap->id);
+		return;
+	}
+
+	ata_port_freeze(ap);
+}
+
 static void ata_eh_scsidone(struct scsi_cmnd *scmd)
 {
 	/* nada */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 13bcb3c..c65cda9 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -223,6 +223,10 @@ enum {
 	ATA_PORT_PRIMARY	= (1 << 0),
 	ATA_PORT_SECONDARY	= (1 << 1),
 
+	/* flags for ata_eh_shduled_port */
+	ATA_EH_ABORT		= (1 << 0), /* abort all active commands */
+	ATA_EH_FREEZE		= (1 << 1), /* freeze port (implies ABORT) */
+
 	/* how hard are we gonna try to probe/recover devices */
 	ATA_PROBE_MAX_TRIES	= 3,
 };
@@ -652,6 +656,7 @@ extern unsigned long ata_pci_default_fil
  */
 extern int ata_scsi_error(struct Scsi_Host *host);
 extern void ata_eng_timeout(struct ata_port *ap);
+extern void ata_eh_schedule_port(struct ata_port *ap, unsigned int flags);
 extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
 extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
 
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/13] libata: implement ata_port_freeze()
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 02/13] libata: add new EH operations Tejun Heo
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Freezing is performed atomic w.r.t. host_set->lock and once frozen
LLDD is not allowed to access the port or any qc on it.  Also, libata
makes sure that no new qc gets issued to a frozen port.

A frozen port is thawed after a reset operation completes
successfully, so reset methods must do its job while the port is
frozen.  During initialization all ports get frozen before requesting
IRQ, so reset methods are always invoked on a frozen port.

Optional ->freeze operation notifies LLDD that the port is being
frozen.  LLDD can disable hardware interrupt in this callback if the
controller's IRQ mask can be changed dynamically.  If the controller
doesn't allow such operation, LLDD can check for frozen state in the
interrupt handler and ack/clear interrupts unconditionally while
frozen.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |   50 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libata.h      |    1 +
 2 files changed, 51 insertions(+), 0 deletions(-)

9f3913439575c8e389f3bc3d1878fdfb0f2f90cc
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 446ab53..69a069f 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -988,6 +988,12 @@ unsigned ata_exec_internal(struct ata_po
 
 	spin_lock_irqsave(&ap->host_set->lock, flags);
 
+	/* no internal command while frozen */
+	if (ap->flags & ATA_FLAG_FROZEN) {
+		spin_unlock_irqrestore(&ap->host_set->lock, flags);
+		return AC_ERR_SYSTEM;
+	}
+
 	/* initialize internal qc */
 
 	/* XXX: Tag 0 is used for drivers with legacy EH as some
@@ -1659,6 +1665,39 @@ void ata_port_disable(struct ata_port *a
 }
 
 /**
+ *	ata_port_freeze - freeze port until reset thaws it
+ *	@ap: ATA port to freeze
+ *
+ *	This function is called when HSM violation or some other
+ *	condition disrupts normal operation of the port.  Frozen port
+ *	is not allowed to perform any operation until a reset puts it
+ *	into known state and thaws it.
+ *
+ *	ap->ops->freeze() callback can be used for freezing the port
+ *	hardware-wise (e.g. mask interrupt / stop DMA engine).  If a
+ *	port cannot be frozen hardware-wise, the interrupt handler
+ *	must ack and clear interrupts unconditionally while the port
+ *	is frozen.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_port_freeze(struct ata_port *ap)
+{
+	if (!ap->ops->error_handler)
+		return;
+
+	WARN_ON(ap->flags & ATA_FLAG_FROZEN);
+
+	if (ap->ops->freeze)
+		ap->ops->freeze(ap);
+
+	ap->flags |= ATA_FLAG_FROZEN;
+
+	DPRINTK("ata%u port frozen\n", ap->id);
+}
+
+/**
  *	ata_down_sata_spd_limit - adjust SATA spd limit downward
  *	@ap: Port to adjust SATA spd limit for
  *
@@ -2617,6 +2656,7 @@ int ata_do_reset(struct ata_port *ap,
 		 ata_reset_fn_t reset, ata_postreset_fn_t postreset,
 		 int verbose, unsigned int *classes)
 {
+	unsigned long flags;
 	int i, rc;
 
 	for (i = 0; i < ATA_MAX_DEVICES; i++)
@@ -2626,6 +2666,11 @@ int ata_do_reset(struct ata_port *ap,
 	if (rc)
 		return rc;
 
+	/* a successful reset thaws the port */
+	spin_lock_irqsave(&ap->host_set->lock, flags);
+	ap->flags &= ~ATA_FLAG_FROZEN;
+	spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
 	/* If any class isn't ATA_DEV_UNKNOWN, consider classification
 	 * is complete and convert all ATA_DEV_UNKNOWN to
 	 * ATA_DEV_NONE.
@@ -4052,6 +4097,10 @@ static struct ata_queued_cmd *ata_qc_new
 	struct ata_queued_cmd *qc = NULL;
 	unsigned int i;
 
+	/* no command while frozen */
+	if (unlikely(ap->flags & ATA_FLAG_FROZEN))
+		return NULL;
+
 	/* the last tag is reserved for internal command. */
 	for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
 		if (!test_and_set_bit(i, &ap->qactive)) {
@@ -4764,6 +4813,7 @@ int ata_device_add(const struct ata_prob
 
 		ata_chk_status(ap);
 		host_set->ops->irq_clear(ap);
+		ata_port_freeze(ap);
 		count++;
 	}
 
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index 31efc2e..681b203 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -50,6 +50,7 @@ extern void ata_port_flush_task(struct a
 extern unsigned ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
 				  struct ata_taskfile *tf, const u8 *cdb,
 				  int dma_dir, void *buf, unsigned int buflen);
+extern void ata_port_freeze(struct ata_port *ap);
 extern int ata_down_sata_spd_limit(struct ata_port *ap);
 extern int ata_set_sata_spd_needed(struct ata_port *ap);
 extern int ata_down_xfermask_limit(struct ata_port *ap, struct ata_device *dev,
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/13] libata: use special reserved tag and qc for internal commands
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (4 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 06/13] libata: implement new EH scheduling via error completion Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 05/13] libata: update ata_qc_from_tag() to enforce normal/EH qc ownership Tejun Heo
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

New EH may issue internal commands to recover from error while failed
qc's are still hanging around.  To allow such usage, reserve tag
ATA_MAX_QUEUE-1 for internal command.  This also makes it easy to tell
whether a qc is for internal command or not.  ata_tag_internal() test
implements this test.

To avoid breaking existing drivers, ata_exec_internal() uses
ATA_TAG_INTERNAL only for drivers which implement ->error_handler.
For drivers on old EH, tag 0 is used as before.  Note that this makes
ata_tag_internal() test valid only when ->error_handler is
implemented.  This is okay as drivers on old EH should not and does
not have any reason to use ata_tag_internal().

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |   32 +++++++++++++++++++++++++++++---
 include/linux/libata.h     |    9 ++++++++-
 2 files changed, 37 insertions(+), 4 deletions(-)

80ff3bcf9ab141553bfc5adbf2783c8e4cdf6375
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 966be30..446ab53 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -981,15 +981,39 @@ unsigned ata_exec_internal(struct ata_po
 {
 	u8 command = tf->command;
 	struct ata_queued_cmd *qc;
+	unsigned int tag, preempted_tag;
 	DECLARE_COMPLETION(wait);
 	unsigned long flags;
 	unsigned int err_mask;
 
 	spin_lock_irqsave(&ap->host_set->lock, flags);
 
-	qc = ata_qc_new_init(ap, dev);
-	BUG_ON(qc == NULL);
+	/* initialize internal qc */
 
+	/* XXX: Tag 0 is used for drivers with legacy EH as some
+	 * drivers choke if any other tag is given.  This breaks
+	 * ata_tag_internal() test for those drivers.  Don't use new
+	 * EH stuff without converting to it.
+	 */
+	if (ap->ops->error_handler)
+		tag = ATA_TAG_INTERNAL;
+	else
+		tag = 0;
+
+	if (test_and_set_bit(tag, &ap->qactive))
+		BUG();
+	qc = ata_qc_from_tag(ap, tag);
+
+	qc->tag = tag;
+	qc->scsicmd = NULL;
+	qc->ap = ap;
+	qc->dev = dev;
+	ata_qc_reinit(qc);
+
+	preempted_tag = ap->active_tag;
+	ap->active_tag = ATA_TAG_POISON;
+
+	/* prepare & issue qc */
 	qc->tf = *tf;
 	if (cdb)
 		memcpy(qc->cdb, cdb, ATAPI_CDB_LEN);
@@ -1031,6 +1055,7 @@ unsigned ata_exec_internal(struct ata_po
 	err_mask = qc->err_mask;
 
 	ata_qc_free(qc);
+	ap->active_tag = preempted_tag;
 
 	/* XXX - Some LLDDs (sata_mv) disable port on command failure.
 	 * Until those drivers are fixed, we detect the condition
@@ -4027,7 +4052,8 @@ static struct ata_queued_cmd *ata_qc_new
 	struct ata_queued_cmd *qc = NULL;
 	unsigned int i;
 
-	for (i = 0; i < ATA_MAX_QUEUE; i++)
+	/* the last tag is reserved for internal command. */
+	for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
 		if (!test_and_set_bit(i, &ap->qactive)) {
 			qc = ata_qc_from_tag(ap, i);
 			break;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 0858788..ccd8846 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -108,7 +108,9 @@ enum {
 	LIBATA_MAX_PRD		= ATA_MAX_PRD / 2,
 	ATA_MAX_PORTS		= 8,
 	ATA_DEF_QUEUE		= 1,
-	ATA_MAX_QUEUE		= 1,
+	/* tag ATA_MAX_QUEUE - 1 is reserved for internal commands */
+	ATA_MAX_QUEUE		= 2,
+	ATA_TAG_INTERNAL	= ATA_MAX_QUEUE - 1,
 	ATA_MAX_SECTORS		= 200,	/* FIXME */
 	ATA_MAX_BUS		= 2,
 	ATA_DEF_BUSY_WAIT	= 10000,
@@ -696,6 +698,11 @@ static inline unsigned int ata_tag_valid
 	return (tag < ATA_MAX_QUEUE) ? 1 : 0;
 }
 
+static inline unsigned int ata_tag_internal(unsigned int tag)
+{
+	return tag == ATA_MAX_QUEUE - 1;
+}
+
 static inline unsigned int ata_class_enabled(unsigned int class)
 {
 	return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI;
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/13] libata: implement new EH scheduling via error completion
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (3 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 08/13] libata: implement new EH scheduling via timeout Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 03/13] libata: use special reserved tag and qc for internal commands Tejun Heo
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

There are several ways a qc can get schedule for EH in new EH.  This
patch implements one of them - completing qc with non-zero
qc->err_mask.  ALL normal qc's with set err_mask are to be examined by
EH.  There's no sideway.

New EH schedules a qc for EH from completion iff ->error_handler is
implemented, 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.

Note that in the new EH, there is no way a qc can hit
ata_qc_complete() twice or normal path completes a qc which has been
scheduled for EH by other entities.  The ownership is clear and must
be followed.  Violation will trigger WARN_ON().

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>

---

 drivers/scsi/libata-core.c |   52 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/scsi/libata-eh.c   |   29 +++++++++++++++++++++++++
 drivers/scsi/libata.h      |    2 ++
 include/linux/libata.h     |   21 +-----------------
 4 files changed, 83 insertions(+), 21 deletions(-)

c0e01e524e5e8ea9924efafea8ec72fe8ea12782
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 884f771..600b323 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -4185,6 +4185,56 @@ void __ata_qc_complete(struct ata_queued
 	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.
+	 *
+	 * When a qc fails, it's marked with EH_SCHEDULED.  In new EH,
+	 * regular 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 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(qc->flags & ATA_QCFLAG_EH_SCHEDULED ||
+			ap->flags & ATA_FLAG_FROZEN);
+
+		if (unlikely(qc->err_mask)) {
+			/* ATA_QCFLAG_FAILED is set for all failed
+			 * qc's including internal qc.
+			 */
+			qc->flags |= ATA_QCFLAG_FAILED;
+			if (!ata_tag_internal(qc->tag)) {
+				ata_eh_schedule_qc(qc);
+				return;
+			}
+		}
+		__ata_qc_complete(qc);
+	} else {
+		if (qc->flags & ATA_QCFLAG_EH_SCHEDULED)
+			return;
+		__ata_qc_complete(qc);
+	}
+}
+
 static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
@@ -5124,7 +5174,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 e73f561..a1fe14f 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -210,6 +210,35 @@ void ata_eng_timeout(struct ata_port *ap
 	DPRINTK("EXIT\n");
 }
 
+/**
+ *	ata_eh_schedule_qc - schedule qc for error handling
+ *	@qc: command to schedule error handling for
+ *
+ *	Schedule error handling for the specified qc.  EH will kick in
+ *	as soon as other commands are drained.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host_set lock)
+ */
+void ata_eh_schedule_qc(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+
+	WARN_ON(!ap->ops->error_handler);
+	WARN_ON(qc->flags & ATA_QCFLAG_EH_SCHEDULED ||
+		ap->flags & ATA_FLAG_FROZEN);
+
+	qc->flags |= ATA_QCFLAG_FAILED | ATA_QCFLAG_EH_SCHEDULED;
+	qc->dev->flags |= ATA_DFLAG_FAILED;
+
+	/* The following will fail if timeout has already expired.
+	 * ata_scsi_timed_out() will put @qc onto EH.  Note that
+	 * EH_SCHEDULED flag is unconditionally set after this
+	 * function completes.
+	 */
+	scsi_eh_schedule_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 681b203..826010c 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -62,6 +62,7 @@ extern int ata_do_reset(struct ata_port 
 			int verbose, 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);
@@ -105,5 +106,6 @@ extern void ata_scsi_rbuf_fill(struct at
 
 /* libata-eh.c */
 extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
+extern void ata_eh_schedule_qc(struct ata_queued_cmd *qc);
 
 #endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 289ec2e..13bcb3c 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -592,7 +592,7 @@ extern void ata_bmdma_start (struct ata_
 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_port *ap, struct ata_device *dev,
 			      struct scsi_cmnd *cmd,
 			      void (*done)(struct scsi_cmnd *));
@@ -858,25 +858,6 @@ static inline void ata_qc_reinit(struct 
 }
 
 /**
- *	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)
-{
-	if (unlikely(qc->flags & ATA_QCFLAG_EH_SCHEDULED))
-		return;
-
-	__ata_qc_complete(qc);
-}
-
-/**
  *	ata_irq_on - Enable interrupts on a port.
  *	@ap: Port on which interrupts are enabled.
  *
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/13] libata: implement new EH scheduling via timeout
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (2 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 07/13] libata: implement ata_eh_schedule_port() Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 06/13] libata: implement new EH scheduling via error completion Tejun Heo
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Implement new EH scheduling from timeout.  ata_scsi_timedout() also
takes care of the race condition in which scsi_eh_schedule_qc() sets
ATA_QCFLAG_EH_SCHEDULED but fails to acutally schedule EH for the qc
because it loses to timeout.

A timeout is HSM violation condition.  New EH assumes that on a
timeout the state of the controller and devices are unknown and
dangerous.  So, all active commands are aborted and the port is
frozen.  Note that commands which get aborted this way don't have its
qc->err_mask set and its retries count will be compensated.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-eh.c |   33 ++++++++++++++++++++++++++++-----
 1 files changed, 28 insertions(+), 5 deletions(-)

d2492ad340d9c93e4042e5fb9be625412dbc926f
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index d443ef2..0a3d832 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -66,19 +66,42 @@ enum scsi_eh_timer_return ata_scsi_timed
 	struct Scsi_Host *host = cmd->device->host;
 	struct ata_port *ap = (struct ata_port *) &host->hostdata[0];
 	unsigned long flags;
+	int i;
 	struct ata_queued_cmd *qc;
 	enum scsi_eh_timer_return ret = EH_HANDLED;
 
 	DPRINTK("ENTER\n");
 
 	spin_lock_irqsave(&ap->host_set->lock, flags);
-	qc = ata_qc_from_tag(ap, ap->active_tag);
-	if (qc) {
-		WARN_ON(qc->scsicmd != cmd);
-		qc->flags |= ATA_QCFLAG_EH_SCHEDULED;
-		qc->err_mask |= AC_ERR_TIMEOUT;
+
+	for (i = 0; i < ATA_MAX_QUEUE; i++) {
+		/* If ata_eh_scheduled_qc() raced with us and lost,
+		 * EH_SCHEDULED flag would already be set, so we
+		 * cannot use ata_qc_from_tag() here.
+		 */
+		qc = __ata_qc_from_tag(ap, i);
+		if (qc && qc->flags & ATA_QCFLAG_ACTIVE && qc->scsicmd == cmd)
+			break;
+	}
+
+	if (i < ATA_MAX_QUEUE) {
+		/* qc->err_mask belongs to the command owner, so it
+		 * cannot be altered here.  Use ATA_QCFLAG_TIMEOUT
+		 * instead.  EH is responsible for merging this flag
+		 * into err_mask after claiming qc ownership.
+		 */
+		qc->flags |= ATA_QCFLAG_TIMEOUT | ATA_QCFLAG_EH_SCHEDULED;
+		qc->dev->flags |= ATA_DFLAG_FAILED;
+
+		if (ap->ops->error_handler)
+			ata_eh_schedule_port(ap, ATA_EH_FREEZE);
+		else
+			/* old EH, do what it used to do */
+			qc->err_mask |= AC_ERR_TIMEOUT;
+
 		ret = EH_NOT_HANDLED;
 	}
+
 	spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
 	DPRINTK("EXIT, ret=%d\n", ret);
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/13] libata: update ata_qc_from_tag() to enforce normal/EH qc ownership
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (5 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 03/13] libata: use special reserved tag and qc for internal commands Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 01/13] libata: add flags for new EH Tejun Heo
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

New EH framework has clear distinction about who owns a qc.  Every qc
starts owned by normal execution path - PIO, interrupt or whatever.
When an exception condition occurs which affects the qc, the qc gets
scheduled for EH.  Note that some events (say, link lost and regained,
command timeout) may schedule qc's which are not directly related but
could have been affected for EH too.  Scheduling for EH is atomic
w.r.t. ap->host_set->lock and once schedule for EH, normal execution
path is not allowed to access the qc in whatever way.  (PIO
synchronization acts a bit different and will be dealt with later)

This patch make ata_qc_from_tag() check whether a qc is active and
owned by normal path before returning it.  If conditions don't match,
NULL is returned and thus access to the qc is denied.
__ata_qc_from_tag() is the original ata_qc_from_tag() and is used by
libata core/EH layers to access inactive/failed qc's.

This change is applied only if the associated LLDD implements new EH
as indicated by non-NULL ->error_handler

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |    4 ++--
 include/linux/libata.h     |   19 +++++++++++++++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

828c4140b9a119c9defbe332ca887e0e1df17e57
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 69a069f..884f771 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -1008,7 +1008,7 @@ unsigned ata_exec_internal(struct ata_po
 
 	if (test_and_set_bit(tag, &ap->qactive))
 		BUG();
-	qc = ata_qc_from_tag(ap, tag);
+	qc = __ata_qc_from_tag(ap, tag);
 
 	qc->tag = tag;
 	qc->scsicmd = NULL;
@@ -4104,7 +4104,7 @@ static struct ata_queued_cmd *ata_qc_new
 	/* the last tag is reserved for internal command. */
 	for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
 		if (!test_and_set_bit(i, &ap->qactive)) {
-			qc = ata_qc_from_tag(ap, i);
+			qc = __ata_qc_from_tag(ap, i);
 			break;
 		}
 
diff --git a/include/linux/libata.h b/include/linux/libata.h
index ccd8846..289ec2e 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -811,14 +811,29 @@ static inline void ata_qc_set_polling(st
 	qc->tf.ctl |= ATA_NIEN;
 }
 
-static inline struct ata_queued_cmd *ata_qc_from_tag (struct ata_port *ap,
-						      unsigned int tag)
+static inline struct ata_queued_cmd *__ata_qc_from_tag(struct ata_port *ap,
+						       unsigned int tag)
 {
 	if (likely(ata_tag_valid(tag)))
 		return &ap->qcmd[tag];
 	return NULL;
 }
 
+static inline struct ata_queued_cmd *ata_qc_from_tag(struct ata_port *ap,
+						     unsigned int tag)
+{
+	struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
+
+	if (unlikely(!qc) || !ap->ops->error_handler)
+		return qc;
+
+	if ((qc->flags & (ATA_QCFLAG_ACTIVE |
+			  ATA_QCFLAG_EH_SCHEDULED)) == ATA_QCFLAG_ACTIVE)
+		return qc;
+
+	return NULL;
+}
+
 static inline void ata_tf_init(struct ata_port *ap, struct ata_taskfile *tf, unsigned int device)
 {
 	memset(tf, 0, sizeof(*tf));
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/13] libata: activate ->post_internal_cmd
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (10 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 13/13] libata: update ata_interrupt() to handle frozen port properly Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 09/13] libata: implement new EH scheduling from PIO Tejun Heo
  2006-04-02 18:34 ` [PATCHSET] new EH framework Tejun Heo
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Update ata_exec_internal() such that it uses new EH framework.
->post_internal_cmd() is always invoked regardless of completion
status.  Also, when ata_exec_internal() detects a timeout condition
and new EH is in place, it freezes the port as timeout for normal
commands would do.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |   17 ++++++++++++-----
 1 files changed, 12 insertions(+), 5 deletions(-)

4e9465fdf6b1bbd2eca5116144724500739239c3
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 1069aa9..2e80930 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -1043,13 +1043,17 @@ unsigned ata_exec_internal(struct ata_po
 
 		/* We're racing with irq here.  If we lose, the
 		 * following test prevents us from completing the qc
-		 * again.  If completion irq occurs after here but
-		 * before the caller cleans up, it will result in a
-		 * spurious interrupt.  We can live with that.
+		 * twice.  If we win, the port is frozen and will be
+		 * cleaned up by ->post_internal_cmd().
 		 */
 		if (qc->flags & ATA_QCFLAG_ACTIVE) {
-			qc->err_mask = AC_ERR_TIMEOUT;
-			ata_qc_complete(qc);
+			qc->err_mask |= AC_ERR_TIMEOUT;
+
+			if (ap->ops->error_handler)
+				ata_eh_schedule_port(ap, ATA_EH_FREEZE);
+			else
+				ata_qc_complete(qc);
+
 			printk(KERN_WARNING "ata%u: qc timeout (cmd 0x%x)\n",
 			       ap->id, command);
 		}
@@ -1057,6 +1061,9 @@ unsigned ata_exec_internal(struct ata_po
 		spin_unlock_irqrestore(&ap->host_set->lock, flags);
 	}
 
+	if (ap->ops->post_internal_cmd)
+		ap->ops->post_internal_cmd(qc);
+
 	*tf = qc->tf;
 	err_mask = qc->err_mask;
 
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/13] libata: implement new EH scheduling from PIO
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (11 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 11/13] libata: activate ->post_internal_cmd Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:34 ` [PATCHSET] new EH framework Tejun Heo
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

PIO executes without holding host_set lock, so it cannot be
synchronized using the same mechanism as interrupt driven execution.
port_task framework makes sure that EH is not entered until PIO task
is flushed, so PIO task can be sure the qc in progress won't go away
underneath it.  One thing it cannot be sure of is whether the qc has
already been scheduled for EH by another exception condition while
host_set lock was released.

This patch makes ata_poll_qc-complete() handle such conditions
properly and make it freeze the port if HSM violation is detected
during PIO execution.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |   23 +++++++++++++++++++----
 1 files changed, 19 insertions(+), 4 deletions(-)

07c8a16be2d6b811c2c33ac31b89c98cea4512bd
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 735f328..1069aa9 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -3472,16 +3472,31 @@ skip_map:
  *	LOCKING:
  *	None.  (grabs host lock)
  */
-
 void ata_poll_qc_complete(struct ata_queued_cmd *qc)
 {
 	struct ata_port *ap = qc->ap;
 	unsigned long flags;
 
 	spin_lock_irqsave(&ap->host_set->lock, flags);
-	ap->flags &= ~ATA_FLAG_NOINTR;
-	ata_irq_on(ap);
-	ata_qc_complete(qc);
+
+	if (ap->ops->error_handler) {
+		/* EH might have kicked in while host_set lock is released */
+		qc = ata_qc_from_tag(ap, qc->tag);
+		if (qc) {
+			if (!(qc->err_mask & AC_ERR_HSM)) {
+				ap->flags &= ~ATA_FLAG_NOINTR;
+				ata_irq_on(ap);
+				ata_qc_complete(qc);
+			} else
+				ata_eh_schedule_port(ap, ATA_EH_FREEZE);
+		}
+	} else {
+		/* old EH */
+		ap->flags &= ~ATA_FLAG_NOINTR;
+		ata_irq_on(ap);
+		ata_qc_complete(qc);
+	}
+
 	spin_unlock_irqrestore(&ap->host_set->lock, flags);
 }
 
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/13] libata: activate ->error_handler
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (8 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 12/13] libata: update SCSI command completion path " Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 13/13] libata: update ata_interrupt() to handle frozen port properly Tejun Heo
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Update ata_scsi_error() such that ->error_handler is invoked if
implemented.  ata_scsi_error() also takes care of merging
ATA_QCFLAG_TIMEOUT into qc->err_mask and clearing SCSI EH conditions.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-eh.c |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

3548a7477edb501d6680730c69f5a5913b81af8b
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 0a3d832..dfbb8c4 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -130,12 +130,29 @@ int ata_scsi_error(struct Scsi_Host *hos
 	spin_unlock_wait(&ap->host_set->lock);
 	ata_port_flush_task(ap);
 
-	WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL);
+	/* invoke error handler */
+	if (ap->ops->error_handler) {
+		int i;
+
+		/* for new EH, all the qc's are ours now */
+		for (i = 0; i < ATA_MAX_QUEUE; i++) {
+			struct ata_queued_cmd *qc;
+			qc = __ata_qc_from_tag(ap, i);
+			if (qc->flags & ATA_QCFLAG_TIMEOUT)
+				qc->err_mask |= AC_ERR_TIMEOUT;;
+		}
 
-	ap->ops->eng_timeout(ap);
+		ap->ops->error_handler(ap);
+	} else {
+		WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL);
+		ap->ops->eng_timeout(ap);
+	}
 
 	WARN_ON(host->host_failed || !list_empty(&host->eh_cmd_q));
 
+	/* finish or retry handled qc's and clean up */
+	host->host_eh_scheduled = 0;
+
 	scsi_eh_flush_done_q(&ap->eh_done_q);
 
 	DPRINTK("EXIT\n");
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/13] libata: update SCSI command completion path for new EH
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (7 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 01/13] libata: add flags for new EH Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 10/13] libata: activate ->error_handler Tejun Heo
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

New EH is responsible for filling qc->tf with status and filling sense
data for ATAPI check sense.  Update SCSI command completion path to
reflect this.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-scsi.c |   25 ++++++++++++++++++++++++-
 1 files changed, 24 insertions(+), 1 deletions(-)

5752d59755f1a27ffb45829cfd41f725eb322121
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 745fc26..bfb9a5b 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -1204,7 +1204,9 @@ static void ata_scsi_qc_complete(struct 
 		if (!need_sense) {
 			cmd->result = SAM_STAT_GOOD;
 		} else {
-			qc->ap->ops->tf_read(qc->ap, &qc->tf);
+			/* new EH already loaded qc->tf */
+			if (!qc->ap->ops->error_handler)
+				qc->ap->ops->tf_read(qc->ap, &qc->tf);
 
 			/* TODO: decide which descriptor format to use
 			 * for 48b LBA devices and call that here
@@ -2069,6 +2071,27 @@ static void atapi_qc_complete(struct ata
 
 	VPRINTK("ENTER, err_mask 0x%X\n", err_mask);
 
+	/* handle completion from new EH */
+	if (unlikely(qc->ap->ops->error_handler &&
+		     (err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID))) {
+
+		if (!(qc->flags & ATA_QCFLAG_SENSE_VALID)) {
+			/* FIXME: not quite right; we don't want the
+			 * translation of taskfile registers into a
+			 * sense descriptors, since that's only
+			 * correct for ATA, not ATAPI
+			 */
+			/* new EH already loaded qc->tf */
+			ata_gen_ata_desc_sense(qc);
+		}
+
+		qc->scsicmd->result = SAM_STAT_CHECK_CONDITION;
+		qc->scsidone(cmd);
+		ata_qc_free(qc);
+		return;
+	}
+
+	/* successful completion or old EH failure path */
 	if (unlikely(err_mask & AC_ERR_DEV)) {
 		cmd->result = SAM_STAT_CHECK_CONDITION;
 		atapi_request_sense(qc);
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 13/13] libata: update ata_interrupt() to handle frozen port properly
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (9 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 10/13] libata: activate ->error_handler Tejun Heo
@ 2006-04-02 18:31 ` Tejun Heo
  2006-04-02 18:31 ` [PATCH 11/13] libata: activate ->post_internal_cmd Tejun Heo
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:31 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide; +Cc: Tejun Heo

Update the stock interrupt handler such that it unconditionally clears
interrupts from a frozen port.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/libata-core.c |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)

c14f71dfb9bbaca1e2308436b0fda06b5ae9901d
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 2e80930..fc9ae17 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -4502,12 +4502,20 @@ irqreturn_t ata_interrupt (int irq, void
 
 	for (i = 0; i < host_set->n_ports; i++) {
 		struct ata_port *ap;
+		struct ata_queued_cmd *qc;
 
 		ap = host_set->ports[i];
-		if (ap &&
-		    !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) {
-			struct ata_queued_cmd *qc;
+		if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED))
+			continue;
 
+		if (unlikely(ap->flags & ATA_FLAG_FROZEN)) {
+			/* port frozen, ack unconditionally */
+			ata_irq_ack(ap, 0);
+			handled = 1;
+			continue;
+		}
+
+		if (!(ap->flags & ATA_FLAG_NOINTR)) {
 			qc = ata_qc_from_tag(ap, ap->active_tag);
 			if (qc && (!(qc->tf.ctl & ATA_NIEN)) &&
 			    (qc->flags & ATA_QCFLAG_ACTIVE))
-- 
1.2.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCHSET] new EH framework
  2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
                   ` (12 preceding siblings ...)
  2006-04-02 18:31 ` [PATCH 09/13] libata: implement new EH scheduling from PIO Tejun Heo
@ 2006-04-02 18:34 ` Tejun Heo
  13 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-04-02 18:34 UTC (permalink / raw)
  To: jgarzik, alan, albertcc, linux-ide

On Mon, Apr 03, 2006 at 03:31:08AM +0900, Tejun Heo wrote:
> Hello, all.
> 
> New EH, finally.  New EH will be posted as two patchsets -
> eh-framework and eh.  As the name suggests, the first one implements
> EH framework in libata core layer and the second one implements
> helpers, drivers, stock routines for bmdma controllers and converts
> several drivers (ata_piix, ahci, sata_sil) to new EH.
> 
> This is the first take of eh-framework patchset.  This patchset
> contains 13 patches and against
> 
>   upstream [1]
>   + scsi_eh_schedule patchset, take 2 [2][3]
>   + ahci softreset presence detection patch [4]
> 
> Brief description of new EH framework follows.
> 

Will post eh patchset tomorrow.  Still gotta write patch descriptions
for those patches and just cannot stay awake anymore.  See ya
tomorrow.  Good ngiht.

-- 
tejun

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2006-04-02 18:34 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-02 18:31 [PATCHSET] new EH framework Tejun Heo
2006-04-02 18:31 ` [PATCH 04/13] libata: implement ata_port_freeze() Tejun Heo
2006-04-02 18:31 ` [PATCH 02/13] libata: add new EH operations Tejun Heo
2006-04-02 18:31 ` [PATCH 07/13] libata: implement ata_eh_schedule_port() Tejun Heo
2006-04-02 18:31 ` [PATCH 08/13] libata: implement new EH scheduling via timeout Tejun Heo
2006-04-02 18:31 ` [PATCH 06/13] libata: implement new EH scheduling via error completion Tejun Heo
2006-04-02 18:31 ` [PATCH 03/13] libata: use special reserved tag and qc for internal commands Tejun Heo
2006-04-02 18:31 ` [PATCH 05/13] libata: update ata_qc_from_tag() to enforce normal/EH qc ownership Tejun Heo
2006-04-02 18:31 ` [PATCH 01/13] libata: add flags for new EH Tejun Heo
2006-04-02 18:31 ` [PATCH 12/13] libata: update SCSI command completion path " Tejun Heo
2006-04-02 18:31 ` [PATCH 10/13] libata: activate ->error_handler Tejun Heo
2006-04-02 18:31 ` [PATCH 13/13] libata: update ata_interrupt() to handle frozen port properly Tejun Heo
2006-04-02 18:31 ` [PATCH 11/13] libata: activate ->post_internal_cmd Tejun Heo
2006-04-02 18:31 ` [PATCH 09/13] libata: implement new EH scheduling from PIO Tejun Heo
2006-04-02 18:34 ` [PATCHSET] new EH framework Tejun Heo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).